RUST / CHAPTER 10

Traits and replaceable behavior

Advanced

Specify capabilities without coupling clients to one representation.

By the end of this chapter

  • Implement a trait for separate types
  • Distinguish generic from dynamic dispatch
  • Keep semantic promises consistent

The idea

A trait describes behavior that implementing types provide. A generic parameter with a trait bound lets the compiler check that the required methods exist. A trait object such as &dyn Label supports dynamic dispatch through a borrowed interface when the trait is dyn-compatible. Both approaches let client code request a capability rather than inspect concrete type names or internal fields.

A method signature is only part of a contract. A pricing trait should also define units, permitted ranges, and whether computing a price changes state. Default trait methods can supply shared behavior while implementations customize other methods. Choose dynamic dispatch when heterogeneous values must share one interface at runtime; choose a generic function when its concrete type parameter can be selected statically at the call site.

Rust example
trait Price { fn cents(&self)->u32; }
struct Fixed(u32);
impl Price for Fixed {fn cents(&self)->u32{self.0}}
fn total(items:&[&dyn Price])->u32{items.iter().map(|x|x.cents()).sum()}
fn main(){let a=Fixed(120);let b=Fixed(80);println!("{}",total(&[&a,&b]));}
Result
200

Borrowed trait objects expose the price capability, and the client sums their returned values without owning them.

Try it yourself

Define two label-producing types and pass borrowed trait objects to one printing function.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

What does a trait bound T: Price permit the client to assume?