Skip to content
Learn Rust
Theme

Traits (Interfaces)

Abstractions · Lesson 32

A trait is a list of methods a type promises to have. A trait can also include default methods, which a type gets without writing them. You use a trait to say what a function needs from its argument, instead of naming one type.

Coming from GoA trait plays the part of a Go interface: the list of methods a function needs. The difference is that a type must say impl Speaker for Robot; having the right methods is not enough. A trait can also have default methods, which Go interfaces lack. And because the impl is a separate block, you can implement your own trait for types you did not write, such as i32, which Go does not allow.

Coming from KotlinA trait is close to a Kotlin interface with default methods. The impl Speaker for Dog block lives apart from the type, so you can implement your trait for types you did not write, like i32. A Kotlin extension function adds a method to such a type but does not make it a Speaker.

Coming from PythonA trait is like a Protocol with default methods, but a type counts only once you write impl Speaker for Robot, and the compiler checks it, not a separate type checker. (A default in a Protocol reaches only the classes that name it as a base.)

Coming from TypeScriptA trait is like an interface plus the default method bodies you would otherwise put in an abstract class or a mixin. It's nominal, though: having a speak method isn't enough, the type must say impl Speaker for Robot.

Coming from JavaA trait is an interface, default methods included. A function that takes &dyn Speaker accepts any type that implements it, like a parameter of interface type, and T: Speaker is like <T extends Speaker>. The implementation lives in a separate impl Speaker for Dog block, so you can add your own trait to a type you didn't write, such as String, without a wrapper class.

Coming from C#A trait is an interface with default methods. You implement it in a separate impl Speaker for Robot block, which means you can add your trait to someone else's type. A default method can be called on the type itself, where a C# default interface method is only visible through the interface type.

Coming from CC has no interfaces; you'd build a struct of function pointers by hand. A trait is that pattern written down for the compiler, which checks that every type fills in every method. Used as &dyn Speaker, it is a data pointer plus a pointer to such a table.

Coming from C++A trait used through &dyn Speaker plays the part of an abstract base class with virtual methods, but a type does not inherit from it and a trait holds no data members. The same trait can also be a compile-time bound on a generic function, a lot like a C++20 concept, except that a type must say impl Speaker for Robot.

The quick comparison

Rustsrc/main.rsRun ↗
trait Speaker {
fn speak(&self) -> String;
fn shout(&self) -> String {
// default method
self.speak().to_uppercase()
}
}
struct Dog;
struct Robot {
id: u32,
}
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Speaker for Robot {
fn speak(&self) -> String {
format!("beep {}", self.id)
}
}
fn introduce(s: &dyn Speaker) {
// any type that implements Speaker
println!("{} / {}", s.speak(), s.shout());
}
fn main() {
introduce(&Dog);
introduce(&Robot { id: 7 });
}
$ cargo run
woof / WOOF
beep 7 / BEEP 7
package main
import (
"fmt"
"strings"
)
type Speaker interface {
Speak() string
}
type Dog struct{}
type Robot struct{ ID int }
func (Dog) Speak() string { return "woof" }
func (r Robot) Speak() string { return fmt.Sprintf("beep %d", r.ID) }
func shout(s Speaker) string { return strings.ToUpper(s.Speak()) }
func introduce(s Speaker) {
fmt.Println(s.Speak(), "/", shout(s))
}
func main() {
introduce(Dog{})
introduce(Robot{ID: 7})
}
$ go run main.go
woof / WOOF
beep 7 / BEEP 7
interface Speaker {
fun speak(): String
fun shout(): String = speak().uppercase()
}
object Dog : Speaker {
override fun speak() = "woof"
}
class Robot(val id: Int) : Speaker {
override fun speak() = "beep $id"
}
fun introduce(s: Speaker) {
println("${s.speak()} / ${s.shout()}")
}
fun main() {
introduce(Dog)
introduce(Robot(7))
}
$ kotlinc Main.kt -include-runtime -d main.jar && java -jar main.jar
woof / WOOF
beep 7 / BEEP 7
from typing import Protocol
class Speaker(Protocol):
def speak(self) -> str: ...
def shout(self) -> str: # default method
return self.speak().upper()
class Dog(Speaker):
def speak(self) -> str:
return "woof"
class Robot(Speaker):
def __init__(self, id: int) -> None:
self.id = id
def speak(self) -> str:
return f"beep {self.id}"
def introduce(s: Speaker) -> None:
print(f"{s.speak()} / {s.shout()}")
introduce(Dog())
introduce(Robot(7))
$ python3 main.py
woof / WOOF
beep 7 / BEEP 7
// an abstract class can carry a default method, like a trait
abstract class Speaker {
abstract speak(): string;
shout(): string { // default method
return this.speak().toUpperCase();
}
}
class Dog extends Speaker {
speak(): string {
return "woof";
}
}
class Robot extends Speaker {
constructor(private id: number) {
super();
}
speak(): string {
return `beep ${this.id}`;
}
}
function introduce(s: Speaker): void {
console.log(`${s.speak()} / ${s.shout()}`);
}
introduce(new Dog());
introduce(new Robot(7));
$ tsc --strict --module nodenext --types node main.ts && node main.js
woof / WOOF
beep 7 / BEEP 7
public class Main {
interface Speaker {
String speak();
default String shout() { // default method
return speak().toUpperCase();
}
}
record Dog() implements Speaker {
public String speak() {
return "woof";
}
}
record Robot(int id) implements Speaker {
public String speak() {
return "beep " + id;
}
}
static void introduce(Speaker s) {
System.out.println(s.speak() + " / " + s.shout());
}
public static void main(String[] args) {
introduce(new Dog());
introduce(new Robot(7));
}
}
$ javac Main.java && java Main
woof / WOOF
beep 7 / BEEP 7
Introduce(new Dog());
Introduce(new Robot(7));
static void Introduce(ISpeaker s) // any type that implements ISpeaker
{
Console.WriteLine($"{s.Speak()} / {s.Shout()}");
}
interface ISpeaker
{
string Speak();
string Shout() => Speak().ToUpper(); // default method
}
class Dog : ISpeaker
{
public string Speak() => "woof";
}
record Robot(uint Id) : ISpeaker
{
public string Speak() => $"beep {Id}";
}
$ dotnet run
woof / WOOF
beep 7 / BEEP 7
#include <ctype.h>
#include <stdio.h>
// C has no traits: a "Speaker" is a struct of function pointers
struct Speaker {
void (*speak)(const void *self, char *out, size_t n);
};
struct Dog { int unused; };
struct Robot { unsigned id; };
void dog_speak(const void *self, char *out, size_t n) {
(void)self;
snprintf(out, n, "woof");
}
void robot_speak(const void *self, char *out, size_t n) {
const struct Robot *r = self;
snprintf(out, n, "beep %u", r->id);
}
const struct Speaker DOG_SPEAKER = {dog_speak};
const struct Speaker ROBOT_SPEAKER = {robot_speak};
// the "default method": works for any Speaker
void shout(const struct Speaker *sp, const void *self, char *out, size_t n) {
sp->speak(self, out, n);
for (char *p = out; *p; p++) *p = (char)toupper((unsigned char)*p);
}
void introduce(const struct Speaker *sp, const void *self) {
char a[64], b[64];
sp->speak(self, a, sizeof a);
shout(sp, self, b, sizeof b);
printf("%s / %s\n", a, b);
}
int main(void) {
struct Dog dog = {0};
struct Robot robot = {7};
introduce(&DOG_SPEAKER, &dog);
introduce(&ROBOT_SPEAKER, &robot);
return 0;
}
$ gcc -Wall -Wextra main.c -o main && ./main
woof / WOOF
beep 7 / BEEP 7
#include <algorithm>
#include <cctype>
#include <format>
#include <iostream>
#include <string>
class Speaker {
public:
virtual ~Speaker() = default;
virtual std::string speak() const = 0;
std::string shout() const { // shared method built on speak()
std::string s = speak();
std::ranges::transform(s, s.begin(), [](unsigned char c) { return std::toupper(c); });
return s;
}
};
class Dog : public Speaker {
public:
std::string speak() const override { return "woof"; }
};
class Robot : public Speaker {
public:
explicit Robot(unsigned id) : id_(id) {}
std::string speak() const override { return std::format("beep {}", id_); }
private:
unsigned id_;
};
void introduce(const Speaker& s) { // any class that derives from Speaker
std::cout << s.speak() << " / " << s.shout() << "\n";
}
int main() {
introduce(Dog{});
introduce(Robot{7});
}
$ g++ -std=c++20 -Wall -Wextra main.cpp -o main && ./main
woof / WOOF
beep 7 / BEEP 7

What's different

  • &dyn Speaker means "a reference to some type that implements Speaker, decided at runtime". The right speak is looked up while the program runs.
  • There is also a compile-time version: fn introduce(s: &impl Speaker). The compiler knows the exact type at each call, so it needs no lookup. Use dyn when you need a list of mixed types. The generics lesson covers the first, the trait objects lesson the second.
  • format! works like println!, but returns the text as a String instead of printing it.
Gotcha

Having the method but not saying so

Looks fine: in Go, Python and TypeScript it runs fine. Rust won't compile it.

Works in Go. In Rust it's just a rule.Kotlin refuses this too. Same idea, different words.Works in Python. In Rust it's just a rule.Works in TypeScript. In Rust it's just a rule.Java refuses this too. Same idea, different words.C# refuses this too. Same idea, different words.No direct equivalent in C. Just a Rust rule.C++ refuses this too. Same idea, different words.
Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
}
struct Robot {
id: u32,
}
impl Robot {
fn speak(&self) -> String {
format!("beep {}", self.id)
}
}
fn introduce(s: &dyn Speaker) {
println!("{}", s.speak());
}
fn main() {
introduce(&Robot { id: 7 });
}
package main
import "fmt"
type Speaker interface{ Speak() string }
type Robot struct{ ID int }
func (r Robot) Speak() string { return fmt.Sprintf("beep %d", r.ID) }
func introduce(s Speaker) { fmt.Println(s.Speak()) }
func main() {
introduce(Robot{ID: 7})
}
$ go run main.go✓ runs
beep 7
Kotlin✗ refuses too

Kotlin refuses this too: Robot has to declare : Speaker before it can be passed as one.

interface Speaker {
fun speak(): String
}
class Robot(val id: Int) { // never says ": Speaker"
fun speak(): String = "beep $id"
}
fun introduce(s: Speaker) = println(s.speak())
fun main() {
introduce(Robot(7))
}
Main.kt:12:15: error: argument type mismatch: actual type is 'Robot', but 'Speaker' was expected.
    introduce(Robot(7))
              ^^^^^^^^
class Robot:
def __init__(self, id: int):
self.id = id
def speak(self) -> str:
return f"beep {self.id}"
def introduce(s):
print(s.speak())
introduce(Robot(7))
$ python3 main.py✓ runs
beep 7
interface Speaker {
speak(): string;
}
class Robot { // never says "implements Speaker"
constructor(private id: number) {}
speak(): string {
return `beep ${this.id}`;
}
}
function introduce(s: Speaker): void {
console.log(s.speak());
}
introduce(new Robot(7));
$ tsc --strict --module nodenext --types node main.ts && node main.js✓ runs
beep 7
Java✗ refuses too

Java refuses this too: Robot has a speak method but never says implements Speaker, so it is not a Speaker.

public class Main {
interface Speaker {
String speak();
}
record Robot(int id) {
String speak() {
return "beep " + id;
}
}
static void introduce(Speaker s) {
System.out.println(s.speak());
}
public static void main(String[] args) {
introduce(new Robot(7));
}
}
Main.java:17: error: incompatible types: Robot cannot be converted to Speaker
        introduce(new Robot(7));
                  ^
Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output
1 error
C#✗ refuses too

C# refuses this too: having a Speak method is not enough, the class must say : ISpeaker (CS1503). Interfaces are opt-in by name in C#, as in Java.

Introduce(new Robot(7));
static void Introduce(ISpeaker s)
{
Console.WriteLine(s.Speak());
}
interface ISpeaker
{
string Speak();
}
record Robot(uint Id) // has Speak, but never says ": ISpeaker"
{
public string Speak() => $"beep {Id}";
}
Program.cs(1,11): error CS1503: Argument 1: cannot convert from 'Robot' to 'ISpeaker'
Cno equivalent

C has no traits or interfaces. A C "Speaker" is a struct of function pointers you fill in by hand, so there is no declaration to forget.

C++✗ refuses too

C++ refuses this too: Robot doesn't inherit from Speaker, so it can't be passed as a const Speaker&. A template or concept would accept it, since those only check that speak() exists.

#include <format>
#include <iostream>
#include <string>
class Speaker {
public:
virtual ~Speaker() = default;
virtual std::string speak() const = 0;
};
class Robot {
public:
unsigned id;
std::string speak() const { return std::format("beep {}", id); }
};
void introduce(const Speaker& s) {
std::cout << s.speak() << "\n";
}
int main() {
introduce(Robot{7});
}
main.cpp: In function ‘int main()’:
main.cpp:22:15: error: invalid initialization of reference of type ‘const Speaker&’ from expression of type ‘Robot’
   22 |     introduce(Robot{7});
      |               ^~~~~~~~
main.cpp:17:31: note: in passing argument 1 of ‘void introduce(const Speaker&)’
   17 | void introduce(const Speaker& s) {
      |                ~~~~~~~~~~~~~~~^
What does the compiler say? Guess first, then open.
$ cargo buildWhat is E0277?
error[E0277]: the trait bound `Robot: Speaker` is not satisfied
  --> src/main.rs:20:15
   |
20 |     introduce(&Robot { id: 7 });
   |               ^^^^^^^^^^^^^^^^ unsatisfied trait bound
   |
help: the trait `Speaker` is not implemented for `Robot`
  --> src/main.rs:5:1
   |
 5 | struct Robot {
   | ^^^^^^^^^^^^
help: this trait has no implementations, consider adding one
  --> src/main.rs:1:1
   |
 1 | trait Speaker {
   | ^^^^^^^^^^^^^
   = note: required for the cast from `&Robot` to `&dyn Speaker`
What happens in Go

Go runs this and prints beep 7. Having a Speak method is enough to count as a Speaker, with nothing to declare.

What happens in Kotlin

Kotlin refuses this too: Robot has to declare : Speaker before it can be passed as one.

What happens in Python

Python prints beep 7. Python uses duck typing: anything with a speak method works, with no need to declare that Robot is a Speaker.

What happens in TypeScript

Prints beep 7. TypeScript checks the shape: Robot has a matching speak, so it counts as a Speaker without saying implements.

What happens in Java

Java refuses this too: Robot has a speak method but never says implements Speaker, so it is not a Speaker.

What happens in C#

C# refuses this too: having a Speak method is not enough, the class must say : ISpeaker (CS1503). Interfaces are opt-in by name in C#, as in Java.

What happens in C

C has no traits or interfaces. A C "Speaker" is a struct of function pointers you fill in by hand, so there is no declaration to forget.

What happens in C++

C++ refuses this too: Robot doesn't inherit from Speaker, so it can't be passed as a const Speaker&. A template or concept would accept it, since those only check that speak() exists.

What Rust sees

Rust wants you to say it: impl Speaker for Robot. A method with a matching name is not enough.

Why Rust says no

One type can then have two methods with the same name that mean different things: the lesson's Car has a start from Vehicle and another from Appliance, and a call says which. It also lets you add a trait to a type later, in a separate place, which is how impl Speaker for i32 works.

The fix

Compiles and runs.

Rust, fixedsrc/main.rsRun ↗
trait Speaker {
fn speak(&self) -> String;
}
struct Robot {
id: u32,
}
impl Speaker for Robot {
fn speak(&self) -> String {
format!("beep {}", self.id)
}
}
fn introduce(s: &dyn Speaker) {
println!("{}", s.speak());
}
fn main() {
introduce(&Robot { id: 7 });
}
$ cargo run
beep 7

The toolkit

A trait names a set of methods. A type that has those methods, and says so, can be used wherever the trait is asked for. It is what Rust uses where other languages use interfaces, abstract base classes or protocols. Outputs come from Linux on 64-bit ARM, Rust 1.99.1.

Defining a trait and implementing it

Rustsrc/main.rsRun ↗
trait Speaker {
fn speak(&self) -> String; // required: every type writes its own
fn shout(&self) -> String {
// default: built on the required method
self.speak().to_uppercase()
}
}
struct Dog;
struct Robot {
id: u32,
}
struct Parrot {
phrase: String,
}
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Speaker for Robot {
fn speak(&self) -> String {
format!("beep {}", self.id)
}
}
impl Speaker for Parrot {
fn speak(&self) -> String {
self.phrase.clone()
}
fn shout(&self) -> String {
// replaces the default
format!("{}!!", self.speak())
}
}
fn main() {
let robot = Robot { id: 7 };
let parrot = Parrot {
phrase: String::from("hello"),
};
println!("dog.speak(): {}", Dog.speak());
println!("dog.shout(): {}", Dog.shout());
println!("robot.shout(): {}", robot.shout());
println!("parrot.shout(): {}", parrot.shout());
}
$ cargo run
dog.speak(): woof
dog.shout(): WOOF
robot.shout(): BEEP 7
parrot.shout(): hello!!
  • trait Speaker { ... } lists methods. speak has no body: every type that implements the trait must write one. shout has a body: a default method, used unless a type replaces it.
  • impl Speaker for Dog { ... } is a block of its own, apart from the struct. The trait is not part of the type’s definition, so it can be added later, and in another place.
  • Parrot replaces shout, and Dog and Robot keep the default. The default calls self.speak(), so it works for any type.
  • The first parameter means what it does in Methods.
  • Once the trait is in scope, Dog.speak() is an ordinary method call (scope, below).

A replacement cannot call the default it replaces. There is no super. Writing Speaker::shout(self) inside Parrot’s shout names Parrot’s own shout, so it calls itself until the stack is gone:

Rustsrc/main.rs! exits with code -6Run ↗
trait Speaker {
fn speak(&self) -> String;
fn shout(&self) -> String {
self.speak().to_uppercase()
}
}
struct Parrot;
impl Speaker for Parrot {
fn speak(&self) -> String {
String::from("hello")
}
// There is no `super` call. This names Parrot's own shout again.
#[allow(unconditional_recursion)]
fn shout(&self) -> String {
format!("{}!!", Speaker::shout(self))
}
}
fn main() {
println!("{}", Parrot.shout());
}
$ cargo run
thread 'main' (1) has overflowed its stack
fatal runtime error: stack overflow, aborting

Move the shared part into another method or a plain function.

A trait can hold more than methods:

A trait can list Example Shown in
Methods, with or without a default body fn speak(&self) -> String; first-trait.rs above
Associated functions, with no self fn make() -> String; two traits
Associated types and constants type Item;, const LIMIT: u32; associated items
Fields not allowed below
Rustsrc/main.rs✗ won't compile
trait Named {
name: String,
}
fn main() {}
$ cargo build
error: expected one of `!` or `::`, found `:`
 --> src/main.rs:2:9
  |
1 | trait Named {
  |             - while parsing this item list starting here
2 |     name: String,
  |         ^ expected one of `!` or `::`
3 | }
  | - the item list ends here

A trait body has no fields. When the data matters, the trait asks for a method (fn name(&self) -> &str) and each type decides where the data is stored.

Asking for a trait: bounds

A function says which trait it needs from its argument. That requirement is a bound.

Rustsrc/main.rsRun ↗
use std::fmt::Display;
trait Speaker {
fn speak(&self) -> String;
}
struct Dog;
struct Robot {
id: u32,
}
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Speaker for Robot {
fn speak(&self) -> String {
format!("beep {}", self.id)
}
}
impl Display for Robot {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "robot #{}", self.id)
}
}
// 1. a type parameter with a bound
fn intro_a<T: Speaker>(s: &T) -> String {
format!("a: {}", s.speak())
}
// 2. the same, written as `impl Trait` in the parameter
fn intro_b(s: &impl Speaker) -> String {
format!("b: {}", s.speak())
}
// 3. the same again, with the bound moved into a where clause
fn intro_c<T>(s: &T) -> String
where
T: Speaker,
{
format!("c: {}", s.speak())
}
// several bounds are joined with +
fn label<T: Speaker + Display>(s: &T) -> String {
format!("{} says {}", s, s.speak())
}
fn main() {
println!("{}", intro_a(&Dog));
println!("{}", intro_b(&Dog));
println!("{}", intro_c(&Robot { id: 7 }));
println!("{}", label(&Robot { id: 7 }));
}
$ cargo run
a: woof
b: woof
c: beep 7
robot #7 says beep 7
  • T: Speaker reads “T is some type that implements Speaker”. T is a type parameter, a placeholder the caller fills in (Generics).
  • &impl Speaker is the same bound without naming T. where T: Speaker is the same bound moved after the signature, which helps when a signature gets long.
  • + asks for several traits: T: Speaker + Display.
  • Inside the function, the bounds decide which methods T has: intro_a can call speak because of its bound, and nothing the bound does not mention.

Each call to intro_a uses one concrete type, chosen by the caller and known when the program is compiled: the compiler produces a copy of the function for each type. That is static dispatch. Going deeper shows the other kind.

When the type has no impl

Pass a type that has not implemented the trait, and the compiler stops at the call:

Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
}
struct Dog;
struct Rock;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
fn introduce<T: Speaker>(s: &T) {
println!("{}", s.speak());
}
fn main() {
introduce(&Dog);
introduce(&Rock);
}
$ cargo buildWhat is E0277?
error[E0277]: the trait bound `Rock: Speaker` is not satisfied
  --> src/main.rs:20:15
   |
20 |     introduce(&Rock);
   |     --------- ^^^^^ unsatisfied trait bound
   |     |
   |     required by a bound introduced by this call
   |
help: the trait `Speaker` is not implemented for `Rock`
  --> src/main.rs:6:1
   |
 6 | struct Rock;
   | ^^^^^^^^^^^
help: the trait `Speaker` is implemented for `Dog`
  --> src/main.rs:8:1
   |
 8 | impl Speaker for Dog {
   | ^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `introduce`
  --> src/main.rs:14:17
   |
14 | fn introduce<T: Speaker>(s: &T) {
   |                 ^^^^^^^ required by this bound in `introduce`

E0277 means “this type lacks something the code asks of it”: here the T: Speaker bound. Calling the method directly gives a different error:

Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
}
struct Dog;
struct Rock;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
fn main() {
println!("{}", Dog.speak());
println!("{}", Rock.speak());
}
$ cargo buildWhat is E0599?
error[E0599]: no method named `speak` found for struct `Rock` in the current scope
  --> src/main.rs:16:25
   |
 6 | struct Rock;
   | ----------- method `speak` not found for this struct
...
16 |     println!("{}", Rock.speak());
   |                         ^^^^^ method not found in `Rock`
   |
   = help: items from traits can only be used if the trait is implemented and in scope
note: `Speaker` defines an item `speak`, perhaps you need to implement it
  --> src/main.rs:1:1
   |
 1 | trait Speaker {
   | ^^^^^^^^^^^^^

E0599 is “no such method”. The hint names the trait that defines speak, so you can implement it. An impl that leaves out a required method is refused with E0046:

Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
fn volume(&self) -> u8;
}
struct Dog;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
fn main() {
println!("{}", Dog.speak());
}
$ cargo buildWhat is E0046?
error[E0046]: not all trait items implemented, missing: `volume`
 --> src/main.rs:8:1
  |
3 |     fn volume(&self) -> u8;
  |     ----------------------- `volume` from trait
...
8 | impl Speaker for Dog {
  | ^^^^^^^^^^^^^^^^^^^^ missing `volume` in implementation

A trait’s methods and scope

To call a trait’s method on a concrete type, the trait must be in scope in the module where you write the call. A mod block in the same file is another scope (a named container of code, covered in Modules):

Rustsrc/main.rs✗ won't compile
mod zoo {
pub trait Speaker {
fn speak(&self) -> String;
}
pub struct Dog;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
}
fn main() {
let dog = zoo::Dog;
println!("{}", dog.speak());
}
$ cargo buildWhat is E0599?
error[E0599]: no method named `speak` found for struct `Dog` in the current scope
  --> src/main.rs:17:24
   |
 3 |         fn speak(&self) -> String;
   |            ----- the method is available for `Dog` here
...
 6 |     pub struct Dog;
   |     -------------- method `speak` not found for this struct
...
17 |     println!("{}", dog.speak());
   |                        ^^^^^ method not found in `Dog`
   |
   = help: items from traits can only be used if the trait is in scope
help: trait `Speaker` which provides `speak` is implemented but not in scope; perhaps you want to import it
   |
 1 + use crate::zoo::Speaker;

The impl exists, and the compiler suggests use crate::zoo::Speaker;. Two cases need no import: a generic function with a bound T: zoo::Speaker, and a &dyn zoo::Speaker. The bound names the trait, so its methods are reachable. And use ... as _; brings a trait into scope for method calls without taking its name:

Rustsrc/main.rsRun ↗
mod zoo {
pub trait Speaker {
fn speak(&self) -> String;
}
pub struct Dog;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
}
// No `use` here: the bound names the trait, so t.speak() is reachable.
fn generic<T: zoo::Speaker>(t: &T) -> String {
t.speak()
}
// The same for a trait object.
fn dynamic(t: &dyn zoo::Speaker) -> String {
t.speak()
}
mod kennel {
// `as _` brings the trait into scope for method calls without taking its name.
use crate::zoo::Speaker as _;
pub fn call() -> String {
crate::zoo::Dog.speak()
}
}
fn main() {
println!("generic: {}", generic(&zoo::Dog));
println!("dyn: {}", dynamic(&zoo::Dog));
println!("kennel: {}", kennel::call());
}
$ cargo run
generic: woof
dyn: woof
kennel: woof

The prelude (the names every module gets without writing use) already includes Iterator, Clone, Default and other common traits, which is why you do not usually import those. Its page lists them.

Two traits, one method name

Two traits can have a method with the same name. A type’s own method wins over a trait’s. To pick a trait yourself, name it:

Rustsrc/main.rsRun ↗
trait Vehicle {
fn start(&self) -> String;
}
trait Appliance {
fn start(&self) -> String;
fn make() -> String; // no self: an associated function
}
struct Car;
impl Car {
fn start(&self) -> String {
String::from("Car::start (its own method)")
}
}
impl Vehicle for Car {
fn start(&self) -> String {
String::from("Vehicle::start")
}
}
impl Appliance for Car {
fn start(&self) -> String {
String::from("Appliance::start")
}
fn make() -> String {
String::from("Appliance::make")
}
}
fn main() {
let car = Car;
println!("car.start(): {}", car.start());
println!("Vehicle::start(&car): {}", Vehicle::start(&car));
println!("Appliance::start(&car): {}", Appliance::start(&car));
println!("<Car as Appliance>::make(): {}", <Car as Appliance>::make());
}
$ cargo run
car.start(): Car::start (its own method)
Vehicle::start(&car): Vehicle::start
Appliance::start(&car): Appliance::start
<Car as Appliance>::make(): Appliance::make
  • Vehicle::start(&car) calls the Vehicle version, passing the receiver by hand.
  • <Car as Appliance>::make() is the fully qualified form. With no self there is no receiver to guide the choice, so you name the type and the trait.

A type may implement both traits, even when both have a default start. The error comes only from a bare call that could mean either:

Rustsrc/main.rs✗ won't compile
trait Vehicle {
fn start(&self) -> String {
String::from("Vehicle::start")
}
}
trait Appliance {
fn start(&self) -> String {
String::from("Appliance::start")
}
}
struct Car;
// Both impls are accepted, even though both traits have a default `start`.
impl Vehicle for Car {}
impl Appliance for Car {}
fn main() {
println!("{}", Car.start());
}
$ cargo buildWhat is E0034?
error[E0034]: multiple applicable items in scope
  --> src/main.rs:20:24
   |
20 |     println!("{}", Car.start());
   |                        ^^^^^ multiple `start` found
   |
note: candidate #1 is defined in an impl of the trait `Appliance` for the type `Car`
  --> src/main.rs:8:5
   |
 8 |     fn start(&self) -> String {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
note: candidate #2 is defined in an impl of the trait `Vehicle` for the type `Car`
  --> src/main.rs:2:5
   |
 2 |     fn start(&self) -> String {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the method for candidate #1
   |
20 -     println!("{}", Car.start());
20 +     println!("{}", Appliance::start(&Car));
   |
help: disambiguate the method for candidate #2
   |
20 -     println!("{}", Car.start());
20 +     println!("{}", Vehicle::start(&Car));

Traits for types you did not write

You can implement your trait for any type, including i32, &str and Vec<T>:

Rustsrc/main.rsRun ↗
trait Describe {
fn describe(&self) -> String;
}
impl Describe for i32 {
fn describe(&self) -> String {
format!("the number {}", self)
}
}
impl Describe for &str {
fn describe(&self) -> String {
format!("the text {:?}", self)
}
}
impl<T: Describe> Describe for Vec<T> {
fn describe(&self) -> String {
let parts: Vec<String> = self.iter().map(|item| item.describe()).collect();
format!("a list of [{}]", parts.join(", "))
}
}
fn main() {
println!("{}", 7.describe());
println!("{}", "hi".describe());
println!("{}", vec![1, 2].describe());
}
$ cargo run
the number 7
the text "hi"
a list of [the number 1, the number 2]

impl<T: Describe> Describe for Vec<T> says “a Vec of describable things is describable”. A blanket impl covers every type that meets a bound:

Rustsrc/main.rsRun ↗
use std::fmt::Display;
trait Shout {
fn shout(&self) -> String;
}
// Every type that implements Display gets Shout, with no per-type impl.
impl<T: Display> Shout for T {
fn shout(&self) -> String {
self.to_string().to_uppercase() + "!"
}
}
struct Robot {
id: u32,
}
impl Display for Robot {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "robot {}", self.id)
}
}
fn main() {
println!("&str: {}", "hello".shout());
println!("i32: {}", 42.shout());
println!("Robot: {}", Robot { id: 7 }.shout());
}
$ cargo run
&str: HELLO!
i32: 42!
Robot: ROBOT 7!

impl<T: Display> Shout for T gave Shout to &str, i32 and Robot at once. The standard library’s ToString reaches every Display type this way.

There is a limit. You may write impl Trait for Type if the trait or the type is defined in your own crate (your package’s code). This is the orphan rule:

Trait Type Allowed? Shown by
yours yours yes first-trait.rs
yours foreign (i32, Vec<T>) yes foreign.rs
foreign (Display) yours yes newtype.rs below
foreign (Display) foreign (Vec<i32>) no, unless a type of yours is among the trait’s own type parameters (next paragraph) orphan.fail.rs below
Rustsrc/main.rs✗ won't compile
use std::fmt;
impl fmt::Display for Vec<i32> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} numbers", self.len())
}
}
fn main() {
println!("{}", vec![1, 2, 3]);
}
$ cargo buildWhat is E0117?
error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
 --> src/main.rs:3:1
  |
3 | impl fmt::Display for Vec<i32> {
  | ^^^^^^^^^^^^^^^^^^^^^^--------
  |                       |
  |                       `Vec` is not defined in the current crate
  |
  = note: impl doesn't have any local type before any uncovered type parameters
  = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules
  = note: define and implement a trait or new type instead

E0117 is the orphan rule. Traits can take type parameters (From<T> does), and impl From<Meters> for f64 is accepted when Meters is your type, because it appears among the trait’s parameters. The Reference states the full rule.

The way around is a newtype: a one-field struct that wraps the foreign type. The wrapper is yours, so the impl is allowed:

Rustsrc/main.rsRun ↗
use std::fmt;
struct Numbers(Vec<i32>); // a wrapper type that belongs to this crate
impl fmt::Display for Numbers {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} numbers", self.0.len())
}
}
fn main() {
println!("{}", Numbers(vec![1, 2, 3]));
}
$ cargo run
3 numbers

Supertraits

A trait can require another trait. Pet: Speaker reads “to be a Pet, a type must also be a Speaker”, so Pet’s methods may call speak:

Rustsrc/main.rsRun ↗
trait Speaker {
fn speak(&self) -> String;
}
// A Pet must also be a Speaker: Pet's own methods may call speak().
trait Pet: Speaker {
fn name(&self) -> String;
fn introduce(&self) -> String {
format!("{} says {}", self.name(), self.speak())
}
}
struct Dog;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Pet for Dog {
fn name(&self) -> String {
String::from("Rex")
}
}
// A bound on Pet also gives access to Speaker's methods.
fn loud<P: Pet>(p: &P) -> String {
p.speak().to_uppercase()
}
fn main() {
println!("introduce(): {}", Dog.introduce());
println!("loud(): {}", loud(&Dog));
}
$ cargo run
introduce(): Rex says woof
loud(): WOOF

A bound on Pet gives access to Speaker’s methods too, as loud shows. This is not inheritance: it is a promise about what else the type implements, and breaking it gives E0277 again:

Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
}
trait Pet: Speaker {
fn name(&self) -> String;
}
struct Cat;
impl Pet for Cat {
fn name(&self) -> String {
String::from("Tom")
}
}
fn main() {
println!("{}", Cat.name());
}
$ cargo buildWhat is E0277?
error[E0277]: the trait bound `Cat: Speaker` is not satisfied
  --> src/main.rs:11:14
   |
11 | impl Pet for Cat {
   |              ^^^ unsatisfied trait bound
   |
help: the trait `Speaker` is not implemented for `Cat`
  --> src/main.rs:9:1
   |
 9 | struct Cat;
   | ^^^^^^^^^^
help: this trait has no implementations, consider adding one
  --> src/main.rs:1:1
   |
 1 | trait Speaker {
   | ^^^^^^^^^^^^^
note: required by a bound in `Pet`
  --> src/main.rs:5:12
   |
 5 | trait Pet: Speaker {
   |            ^^^^^^^ required by this bound in `Pet`

error[E0277]: the trait bound `Cat: Speaker` is not satisfied
  --> src/main.rs:18:24
   |
18 |     println!("{}", Cat.name());
   |                        ^^^^ unsatisfied trait bound
   |
help: the trait `Speaker` is not implemented for `Cat`
  --> src/main.rs:9:1
   |
 9 | struct Cat;
   | ^^^^^^^^^^
help: this trait has no implementations, consider adding one
  --> src/main.rs:1:1
   |
 1 | trait Speaker {
   | ^^^^^^^^^^^^^
note: required by a bound in `Pet::name`
  --> src/main.rs:5:12
   |
 5 | trait Pet: Speaker {
   |            ^^^^^^^ required by this bound in `Pet::name`
 6 |     fn name(&self) -> String;
   |        ---- required by a bound in this associated function

Associated types and constants

A trait can leave a type or a constant for each implementing type to choose:

Rustsrc/main.rsRun ↗
use std::any::type_name;
trait Container {
type Item; // chosen by each implementing type
const KIND: &'static str; // a constant, also chosen per type
fn first(&self) -> Option<&Self::Item>;
}
struct Names(Vec<String>);
struct Counts(Vec<u32>);
impl Container for Names {
type Item = String;
const KIND: &'static str = "names";
fn first(&self) -> Option<&String> {
self.0.first()
}
}
impl Container for Counts {
type Item = u32;
const KIND: &'static str = "counts";
fn first(&self) -> Option<&u32> {
self.0.first()
}
}
fn show<C: Container>(c: &C) {
println!(
"{}: Item is {}, first is {}",
C::KIND,
type_name::<C::Item>(),
c.first().is_some()
);
}
fn main() {
show(&Names(vec![String::from("ana")]));
show(&Counts(vec![]));
}
$ cargo run
names: Item is alloc::string::String, first is true
counts: Item is u32, first is false

C::Item means “the Item of whichever C this is”, and C::KIND reads the constant. &'static str is the type of a string literal such as "names" (Lifetimes explains 'static). alloc::string::String is the full path of the standard library’s String.

Iterator has a type Item like this (Iterators writes one). In Java, Iterator<T> lets the user of the interface pick T. Here each implementing type writes its Item once, so the trait is plain Iterator.

Returning “something that implements a trait”

impl Speaker also works as a return type. It hides the type but does not make it vary:

Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
}
struct Dog;
struct Robot;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Speaker for Robot {
fn speak(&self) -> String {
String::from("beep")
}
}
fn pick(dog: bool) -> impl Speaker {
if dog { Dog } else { Robot }
}
fn main() {
println!("{}", pick(true).speak());
}
$ cargo buildWhat is E0308?
error[E0308]: `if` and `else` have incompatible types
  --> src/main.rs:21:27
   |
21 |     if dog { Dog } else { Robot }
   |              ---          ^^^^^ expected `Dog`, found `Robot`
   |              |
   |              expected because of this
   |
help: you could change the return type to be a boxed trait object
   |
20 - fn pick(dog: bool) -> impl Speaker {
20 + fn pick(dog: bool) -> Box<dyn Speaker> {
   |
help: if you change the return type to expect trait objects, box the returned expressions
   |
21 |     if dog { Box::new(Dog) } else { Box::new(Robot) }
   |              +++++++++   +          +++++++++     +

impl Speaker means one concrete type that the compiler knows and the caller does not name. When the answer depends on run-time data, the compiler suggests a trait object: Box<dyn Speaker>, a pointer to any value that implements the trait. Box<T> is an owned pointer to a value on the heap, and dyn marks that the method is looked up while the program runs (Trait objects):

Rustsrc/main.rsRun ↗
trait Speaker {
fn speak(&self) -> String;
}
struct Dog;
struct Robot;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Speaker for Robot {
fn speak(&self) -> String {
String::from("beep")
}
}
// One concrete type per function: the caller sees only "a Speaker".
fn make_dog() -> impl Speaker {
Dog
}
// Different types chosen at run time need a trait object.
fn pick(dog: bool) -> Box<dyn Speaker> {
if dog { Box::new(Dog) } else { Box::new(Robot) }
}
fn main() {
println!("make_dog(): {}", make_dog().speak());
println!("pick(false): {}", pick(false).speak());
}
$ cargo run
make_dog(): woof
pick(false): beep

A trait is not a type, so its bare name cannot stand where a type goes. Programmers used to interfaces write this first:

Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
}
// A trait is not a type: its bare name is refused in all three places.
fn param(s: &Speaker) -> usize {
s.speak().len()
}
fn list(v: Vec<Speaker>) -> usize {
v.len()
}
fn result() -> Speaker {
todo!()
}
fn main() {}
$ cargo buildWhat is E0782?
error[E0782]: expected a type, found a trait
 --> src/main.rs:6:14
  |
6 | fn param(s: &Speaker) -> usize {
  |              ^^^^^^^
  |
help: use a new generic type parameter, constrained by `Speaker`
  |
6 - fn param(s: &Speaker) -> usize {
6 + fn param<T: Speaker>(s: &T) -> usize {
  |
help: you can also use an opaque type, but users won't be able to specify the type parameter when calling the `fn`, having to rely exclusively on type inference
  |
6 | fn param(s: &impl Speaker) -> usize {
  |              ++++
help: alternatively, use a trait object to accept any type that implements `Speaker`, accessing its methods at runtime using dynamic dispatch
  |
6 | fn param(s: &dyn Speaker) -> usize {
  |              +++

error[E0782]: expected a type, found a trait
  --> src/main.rs:10:16
   |
10 | fn list(v: Vec<Speaker>) -> usize {
   |                ^^^^^^^
   |
help: you can add the `dyn` keyword if you want a trait object
   |
10 | fn list(v: Vec<dyn Speaker>) -> usize {
   |                +++

error[E0782]: expected a type, found a trait
  --> src/main.rs:14:16
   |
14 | fn result() -> Speaker {
   |                ^^^^^^^
   |
help: use `impl Speaker` to return an opaque type, as long as you return a single underlying type
   |
14 | fn result() -> impl Speaker {
   |                ++++
help: alternatively, you can return an owned trait object
   |
14 | fn result() -> Box<dyn Speaker> {
   |                +++++++        +

E0782 lists the fixes: a bound, impl, or dyn. Trait objects explains why dyn is needed.

Spelling Where The type is
T: Speaker, &impl Speaker parameter any one type, chosen by the caller
-> impl Speaker result one type, chosen by the function
&dyn Speaker, Box<dyn Speaker> parameter, result, or inside a collection any type, looked up at run time

Translating from what you know

Going deeper

Static and dynamic dispatch in one program

Rustsrc/main.rsRun ↗
use std::any::type_name;
trait Speaker {
fn speak(&self) -> String;
}
struct Dog;
struct Robot {
id: u32,
}
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Speaker for Robot {
fn speak(&self) -> String {
format!("beep {}", self.id)
}
}
// Generic: T is one concrete type, known when the program is compiled.
fn generic<T: Speaker>(s: &T) {
println!("generic: T = {}, says {}", type_name::<T>(), s.speak());
}
fn main() {
generic(&Dog);
generic(&Robot { id: 7 });
// dyn: one list holding different types; the method is looked up while running.
let crowd: Vec<Box<dyn Speaker>> = vec![Box::new(Dog), Box::new(Robot { id: 7 })];
for s in &crowd {
println!("dyn: {}", s.speak());
}
}
$ cargo run
generic: T = main::Dog, says woof
generic: T = main::Robot, says beep 7
dyn: woof
dyn: beep 7
  • generic prints a different T for each call. The compiler knows which speak to call, because each call is for one type. This is static dispatch, and the generics lesson explains how the compiler produces a version for each type. (The text type_name prints is not guaranteed, so use it to look, not to compare.)
  • The Vec<Box<dyn Speaker>> holds a Dog and a Robot side by side, which a Vec<T> cannot. The method is looked up while the program runs. This is dynamic dispatch, and Trait objects explains the lookup.

A plain Vec of two different types is refused for the same reason (all elements of a Vec<T> are one type):

Rustsrc/main.rs✗ won't compile
trait Speaker {
fn speak(&self) -> String;
}
struct Dog;
struct Robot;
impl Speaker for Dog {
fn speak(&self) -> String {
String::from("woof")
}
}
impl Speaker for Robot {
fn speak(&self) -> String {
String::from("beep")
}
}
fn main() {
let crowd = vec![Dog, Robot];
println!("{}", crowd.len());
}
$ cargo buildWhat is E0308?
error[E0308]: mismatched types
  --> src/main.rs:21:27
   |
21 |     let crowd = vec![Dog, Robot];
   |                           ^^^^^ expected `Dog`, found `Robot`

Why the orphan rule exists

Without it, two crates could implement the same trait for the same type, and Rust would not know which to use (the property that prevents this is coherence). The same reasoning applies in your own crate: two impls that could both apply to one type are refused (E0119):

Rustsrc/main.rs✗ won't compile
use std::fmt::Display;
trait Describe {
fn describe(&self) -> String;
}
impl<T: Display> Describe for T {
fn describe(&self) -> String {
format!("something printable: {}", self)
}
}
impl Describe for i32 {
fn describe(&self) -> String {
format!("the number {}", self)
}
}
fn main() {
println!("{}", 7.describe());
}
$ cargo buildWhat is E0119?
error[E0119]: conflicting implementations of trait `Describe` for type `i32`
  --> src/main.rs:13:1
   |
 7 | impl<T: Display> Describe for T {
   | ------------------------------- first implementation here
...
13 | impl Describe for i32 {
   | ^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `i32`

i32 implements Display, so the blanket impl already covers it. The rule’s price is boilerplate: a newtype forwards the methods you need by hand (advanced traits covers the pattern).

derive is a trait impl

Each name in #[derive(Debug, Clone)] is a trait, and the compiler writes the impl block from the fields (Structs). Write one by hand when the generated one is wrong or no derive exists, as for Display; Common traits says which to derive. A trait can also have no methods at all, like Copy: implementing it is a statement about the type (std::marker lists such traits).

A trait or an enum?

Both answer “this value is one of several kinds”; they differ in who can add a kind.

An enum A trait
The kinds listed in one place; adding one means editing the enum any crate can add a type with an impl
match over the kinds the compiler checks that every case is covered not directly: the code sees the trait’s methods, not the concrete type

When you know every kind (the commands of a protocol), an enum lets the compiler check each place that handles them. When other code should add kinds (plugins, a library that calls your code), use a trait. Neither is better in general.

Practice

Try it

Each exercise is a small program with checks in main. Open it in the Playground, replace the todo!(), and run until it prints all checks passed.

Shapes with a default method

Fill in the Shape implementations and the default describe. Triangle’s impl is given and has no describe, which must still work, so write it once, in the trait. Then write biggest (the position of the largest item, or None), which takes a slice of one type, and total_area, which takes a mixed list of &dyn Shape.

Starterexercises/shapes.rs
trait Shape {
fn area(&self) -> f64;
fn name(&self) -> String;
/// Default: "<name> with area <area>", the area with two decimals.
fn describe(&self) -> String {
todo!("format self.name() and self.area() with {{:.2}}")
}
}
struct Square(f64);
struct Circle(f64);
struct Triangle {
base: f64,
height: f64,
}
// Area of a square is side * side; of a circle, PI * radius * radius.
// Names are "square" and "circle".
impl Shape for Square {
fn area(&self) -> f64 {
todo!("side * side")
}
fn name(&self) -> String {
todo!("the name")
}
}
impl Shape for Circle {
fn area(&self) -> f64 {
todo!("PI * radius * radius, with std::f64::consts::PI")
}
fn name(&self) -> String {
todo!("the name")
}
}
// Triangle gives only the two required methods and has no describe().
// Its describe() must still work, so the default belongs in the trait.
impl Shape for Triangle {
fn area(&self) -> f64 {
self.base * self.height / 2.0
}
fn name(&self) -> String {
String::from("triangle")
}
}
/// The position of the item with the largest area, or None for an empty slice.
/// (If two tie, either position is fine.)
fn biggest<T: Shape>(items: &[T]) -> Option<usize> {
todo!("compare areas with total_cmp; return the winner's position")
}
/// The total area of a mixed list: a Square next to a Circle.
/// `&dyn Shape` is a reference to any type that implements Shape.
fn total_area(items: &[&dyn Shape]) -> f64 {
todo!("a slice of &dyn Shape holds different types; add up the areas")
}
fn main() {
assert_eq!(Square(3.0).describe(), "square with area 9.00");
assert_eq!(Circle(1.0).describe(), "circle with area 3.14");
// Triangle never wrote describe(): the default from the trait is used.
let t = Triangle {
base: 3.0,
height: 5.0,
};
assert_eq!(t.describe(), "triangle with area 7.50");
// `biggest` takes a slice of ONE type (T). A mixed list needs `dyn`.
assert_eq!(biggest(&[Square(1.0), Square(3.0), Square(2.0)]), Some(1));
assert_eq!(biggest(&[Circle(0.5), Circle(2.0)]), Some(1));
assert_eq!(biggest(&[Circle(2.0), Circle(0.5)]), Some(0));
// No items, no biggest.
assert_eq!(biggest::<Square>(&[]), None);
let mixed: [&dyn Shape; 3] = [&Square(2.0), &Circle(1.0), &t];
let expected = 4.0 + std::f64::consts::PI + 7.5;
assert!((total_area(&mixed) - expected).abs() < 1e-9);
// The default method works through `dyn` too.
assert_eq!(mixed[2].describe(), "triangle with area 7.50");
assert_eq!(total_area(&[]), 0.0);
println!("all checks passed");
}

Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.

Show a solution
One solutionexercises/shapes.solution.rsRun ↗
trait Shape {
fn area(&self) -> f64;
fn name(&self) -> String;
/// Default: "<name> with area <area>", the area with two decimals.
fn describe(&self) -> String {
format!("{} with area {:.2}", self.name(), self.area())
}
}
struct Square(f64);
struct Circle(f64);
struct Triangle {
base: f64,
height: f64,
}
// Area of a square is side * side; of a circle, PI * radius * radius.
// Names are "square" and "circle".
impl Shape for Square {
fn area(&self) -> f64 {
self.0 * self.0
}
fn name(&self) -> String {
String::from("square")
}
}
impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.0 * self.0
}
fn name(&self) -> String {
String::from("circle")
}
}
// Triangle gives only the two required methods and has no describe().
// Its describe() must still work, so the default belongs in the trait.
impl Shape for Triangle {
fn area(&self) -> f64 {
self.base * self.height / 2.0
}
fn name(&self) -> String {
String::from("triangle")
}
}
/// The position of the item with the largest area, or None for an empty slice.
/// (If two tie, either position is fine.)
fn biggest<T: Shape>(items: &[T]) -> Option<usize> {
items
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.area().total_cmp(&b.area()))
.map(|(position, _)| position)
}
/// The total area of a mixed list: a Square next to a Circle.
/// `&dyn Shape` is a reference to any type that implements Shape.
fn total_area(items: &[&dyn Shape]) -> f64 {
items.iter().map(|shape| shape.area()).sum()
}
fn main() {
assert_eq!(Square(3.0).describe(), "square with area 9.00");
assert_eq!(Circle(1.0).describe(), "circle with area 3.14");
// Triangle never wrote describe(): the default from the trait is used.
let t = Triangle {
base: 3.0,
height: 5.0,
};
assert_eq!(t.describe(), "triangle with area 7.50");
// `biggest` takes a slice of ONE type (T). A mixed list needs `dyn`.
assert_eq!(biggest(&[Square(1.0), Square(3.0), Square(2.0)]), Some(1));
assert_eq!(biggest(&[Circle(0.5), Circle(2.0)]), Some(1));
assert_eq!(biggest(&[Circle(2.0), Circle(0.5)]), Some(0));
// No items, no biggest.
assert_eq!(biggest::<Square>(&[]), None);
let mixed: [&dyn Shape; 3] = [&Square(2.0), &Circle(1.0), &t];
let expected = 4.0 + std::f64::consts::PI + 7.5;
assert!((total_area(&mixed) - expected).abs() < 1e-9);
// The default method works through `dyn` too.
assert_eq!(mixed[2].describe(), "triangle with area 7.50");
assert_eq!(total_area(&[]), 0.0);
println!("all checks passed");
}
$ cargo run
all checks passed

Display for a list of scores

Print a Vec<u32> as scores: 3, 5, 9 (total 17). First try impl Display for Vec<u32> and read the compiler’s answer, then implement Display for the Scores wrapper instead. An empty list prints scores: none.

Starterexercises/scores-display.rs
use std::fmt;
// Goal: print scores as "scores: 3, 5, 9 (total 17)", or "scores: none" when empty.
//
// Step 1. Uncomment this block, put any body in it, and run. Read the error
// (E0117): Display is not yours and Vec<u32> is not yours.
//
// impl fmt::Display for Vec<u32> {
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// write!(f, "scores")
// }
// }
//
// Step 2. Comment it out again and write the impl for the wrapper below,
// a type that IS yours.
struct Scores(Vec<u32>);
impl fmt::Display for Scores {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
todo!("write! the pieces; join the numbers with \", \"; handle the empty case")
}
}
fn main() {
assert_eq!(
Scores(vec![3, 5, 9]).to_string(),
"scores: 3, 5, 9 (total 17)"
);
assert_eq!(Scores(vec![4]).to_string(), "scores: 4 (total 4)");
// Nothing to join, nothing to total.
assert_eq!(Scores(vec![]).to_string(), "scores: none");
// The wrapper still holds a normal Vec.
assert_eq!(Scores(vec![1, 2]).0.len(), 2);
println!("all checks passed");
}

Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.

Show a solution
One solutionexercises/scores-display.solution.rsRun ↗
use std::fmt;
// Goal: print scores as "scores: 3, 5, 9 (total 17)", or "scores: none" when empty.
//
// Step 1. Uncomment this block, put any body in it, and run. Read the error
// (E0117): Display is not yours and Vec<u32> is not yours.
//
// impl fmt::Display for Vec<u32> {
// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// write!(f, "scores")
// }
// }
//
// Step 2. Comment it out again and write the impl for the wrapper below,
// a type that IS yours.
struct Scores(Vec<u32>);
impl fmt::Display for Scores {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.0.is_empty() {
return write!(f, "scores: none");
}
let parts: Vec<String> = self.0.iter().map(|n| n.to_string()).collect();
let total: u32 = self.0.iter().sum();
write!(f, "scores: {} (total {})", parts.join(", "), total)
}
}
fn main() {
assert_eq!(
Scores(vec![3, 5, 9]).to_string(),
"scores: 3, 5, 9 (total 17)"
);
assert_eq!(Scores(vec![4]).to_string(), "scores: 4 (total 4)");
// Nothing to join, nothing to total.
assert_eq!(Scores(vec![]).to_string(), "scores: none");
// The wrapper still holds a normal Vec.
assert_eq!(Scores(vec![1, 2]).0.len(), 2);
println!("all checks passed");
}
$ cargo run
all checks passed

A trait for a type you do not own

Implement Stats for the slice type [i32], then call it on a Vec, an array and a sub-slice (a method call finds the method by borrowing the Vec as a slice). Handle an empty slice, a mean that is not a whole number, and numbers so large that adding them as i32 would overflow.

Starterexercises/stats.rs
trait Stats {
/// The average, or None when there are no numbers.
fn mean(&self) -> Option<f64>;
/// The largest minus the smallest, or None when there are no numbers.
fn spread(&self) -> Option<i64>;
}
// Implement the trait for the slice type [i32], a type you do not own.
// Vec<i32> and arrays can then call these methods too.
impl Stats for [i32] {
fn mean(&self) -> Option<f64> {
todo!("add up as i64 so large numbers cannot overflow, then divide")
}
fn spread(&self) -> Option<i64> {
todo!("max and min return Option; the subtraction must not overflow an i32")
}
}
fn main() {
let v = vec![2, 4, 9];
assert_eq!(v.mean(), Some(5.0));
assert_eq!(v.spread(), Some(7));
// Arrays and sub-slices work too.
assert_eq!([10, 20].mean(), Some(15.0));
// A mean that is not a whole number.
assert_eq!([1, 2].mean(), Some(1.5));
assert_eq!(v[1..].spread(), Some(5));
// Negative numbers.
assert_eq!([-3, 3].spread(), Some(6));
// Large numbers: adding them as i32 would overflow.
assert_eq!([i32::MAX, i32::MAX].mean(), Some(i32::MAX as f64));
assert_eq!([i32::MIN, i32::MAX].spread(), Some(4_294_967_295));
// No numbers, no answer.
let empty: Vec<i32> = vec![];
assert_eq!(empty.mean(), None);
assert_eq!(empty.spread(), None);
println!("all checks passed");
}

Open in the Rust Playground ↗Run it first: it stops at todo!() with not yet implemented and a hint, and the compiler warns that the parameter is unused. Both are expected. Replace the todo!() with your code and run it again until it prints all checks passed.

Show a solution
One solutionexercises/stats.solution.rsRun ↗
trait Stats {
/// The average, or None when there are no numbers.
fn mean(&self) -> Option<f64>;
/// The largest minus the smallest, or None when there are no numbers.
fn spread(&self) -> Option<i64>;
}
// Implement the trait for the slice type [i32], a type you do not own.
// Vec<i32> and arrays can then call these methods too.
impl Stats for [i32] {
fn mean(&self) -> Option<f64> {
if self.is_empty() {
return None;
}
let total: i64 = self.iter().map(|&n| n as i64).sum();
Some(total as f64 / self.len() as f64)
}
fn spread(&self) -> Option<i64> {
let max = *self.iter().max()?;
let min = *self.iter().min()?;
Some(max as i64 - min as i64)
}
}
fn main() {
let v = vec![2, 4, 9];
assert_eq!(v.mean(), Some(5.0));
assert_eq!(v.spread(), Some(7));
// Arrays and sub-slices work too.
assert_eq!([10, 20].mean(), Some(15.0));
// A mean that is not a whole number.
assert_eq!([1, 2].mean(), Some(1.5));
assert_eq!(v[1..].spread(), Some(5));
// Negative numbers.
assert_eq!([-3, 3].spread(), Some(6));
// Large numbers: adding them as i32 would overflow.
assert_eq!([i32::MAX, i32::MAX].mean(), Some(i32::MAX as f64));
assert_eq!([i32::MIN, i32::MAX].spread(), Some(4_294_967_295));
// No numbers, no answer.
let empty: Vec<i32> = vec![];
assert_eq!(empty.mean(), None);
assert_eq!(empty.spread(), None);
println!("all checks passed");
}
$ cargo run
all checks passed

Read more