ProgramGuru

Traits and derived behavior

Traits and derived behavior

Several kinds of school records might provide a label. A trait defines shared behavior that different types can implement, without requiring a common class hierarchy. Derive can generate supported standard trait implementations from a type’s fields.

What you’ll learn

  • Implement shared behavior for a student type.
  • Distinguish diagnostic derives and dynamic trait dispatch.

Traits and derived behavior in action

  1. Student stores a name

    The trait implementation creates a human-facing label.

    Student stores a name
  2. Labeled supplies behavior

    The generated Debug implementation produces a structural diagnostic representation.

    Labeled supplies behavior
  3. Debug supplies diagnostic view

    Both methods borrow the record, so calling one does not prevent the other.

    Debug supplies diagnostic view
The trait implementation creates a human-facing label. The generated Debug implementation produces a structural diagnostic representation. Both methods borrow the record, so calling one does not prevent the other.

Traits and derived behavior explained

Define a trait’s method signature, then implement it for a struct. A method can borrow &self and return owned display text. #[derive(Debug)] generates a Debug implementation if the fields support it. Traits describe supported operations; they do not automatically share field storage between types.

Work through the Rust program

Save the complete source as main.rs in the Rust environment. No standard input is needed.

Rust 2024 · complete program

#[derive(Debug)]
struct Student { name: String }
trait Labeled { fn label(&self) -> String; }
impl Labeled for Student {
  fn label(&self) -> String { format!("Student: {}", self.name) }
}
fn main() {
  let student = Student { name: "Diya".to_string() };
  println!("{}", student.label());
  println!("{student:?}");
}

Expected output

Student: Diya
Student { name: "Diya" }

Follow the execution

The trait implementation creates a human-facing label. The generated Debug implementation produces a structural diagnostic representation. Both methods borrow the record, so calling one does not prevent the other.

Try the complete program in the Rust compiler. For tests, Cargo commands and local files, use the specified local environment.

One trait, two dispatch choices

A generic trait bound allows specialization for a concrete type. A borrowed &dyn Labeled is a trait object, carrying information that selects an implementation at runtime. It supports different compatible implementations behind one interface, with an indirect call and restrictions on which traits support dynamic dispatch.

Rust 2024 · complete program

struct Student { name: String }
trait Labeled { fn label(&self) -> String; }
impl Labeled for Student {
  fn label(&self) -> String { format!("Student: {}", self.name) }
}
fn show(item: &dyn Labeled) { println!("{}", item.label()); }
fn main() {
  let student = Student { name: "Diya".to_string() };
  show(&student);
}

Expected output

Student: Diya

Show borrows the student through its trait interface. Calling label selects the Student implementation, which formats the name. The helper does not own or duplicate the student record.

Try a small variation

Your task: Does implementing Labeled automatically create a name field in every type?

Rust 2024 · complete program

#[derive(Debug)]
struct Student { name: String }
trait Labeled { fn label(&self) -> String; }
impl Labeled for Student {
  fn label(&self) -> String { format!("Student: {}", self.name) }
}
fn main() {
  let student = Student { name: "Diya".to_string() };
  println!("{}", student.label());
  println!("{student:?}");
}

Worked answer

No. Each implementing type supplies the promised behavior using its own representation. A trait is a behavior contract, not a shared storage layout.

Mistakes and limits to recognize

Derive only supports traits that provide a derive implementation and whose requirements are met. Manual implementations can enforce domain-specific behavior. Trait and implementation placement also follows coherence rules; not every external trait can be implemented for every external type.

Check your understanding

Does implementing Labeled automatically create a name field in every type?
Choose one answer. You can try again.

Answer and explanation

No.

No. Each implementing type supplies the promised behavior using its own representation. A trait is a behavior contract, not a shared storage layout.

Take the idea into your next program

No. Each implementing type supplies the promised behavior using its own representation. A trait is a behavior contract, not a shared storage layout. Derive only supports traits that provide a derive implementation and whose requirements are met.

Further reading: the official Rust Book on traits and derived behavior.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Generics and trait bounds →