Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Cheat Sheets
  3. Rust
  4. Rust Traits & Generics Cheat Sheet
RustCheat Sheet

Rust Traits & Generics Cheat Sheet

Quick-reference for trait definitions, generic bounds, trait objects, and derive macros. Each section includes copy-ready snippets with inline output comments.

On this page
  1. 1Defining Traits
  2. 2Implementing Traits
  3. 3Derive Macros
  4. 4Trait Bounds
  5. 5impl Trait
  6. 6Trait Objects (dyn Trait)
  7. 7Essential Std Traits: From, Into, Display, Debug
  8. 8Associated Types vs Generic Parameters
  9. 9Orphan Rule and Newtype Pattern
  10. 10Turbofish and Disambiguation
  11. 11Object Safety
Defining TraitsImplementing TraitsDerive MacrosTrait Boundsimpl TraitTrait Objects (dyn Trait)Essential Std Traits: From, Into, Display, DebugAssociated Types vs Generic ParametersOrphan Rule and Newtype PatternTurbofish and DisambiguationObject Safety

Defining Traits

Traits define shared behavior. They are similar to interfaces in other languages.

Trait with a required method
trait Summary {
    fn summarize(&self) -> String;
}
Trait with a default method
trait Summary {
    fn summarize(&self) -> String {
        String::from("(read more...)")
    }
}
// Implementors can override or use the default
Trait with multiple methods
trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(by {})", self.summarize_author())
    }
}

Default methods can call other methods in the same trait, even required ones.

Implementing Traits

Use impl Trait for Type to provide the behavior. Every required method must be implemented.

Basic implementation
struct Article { title: String, author: String }

impl Summary for Article {
    fn summarize(&self) -> String {
        format!("{} by {}", self.title, self.author)
    }
}

let a = Article { title: "Rust 2026".into(), author: "Alice".into() };
println!("{}", a.summarize());  // => "Rust 2026 by Alice"
Implementing standard library traits
use std::fmt;

struct Point { x: i32, y: i32 }

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

println!("{}", Point { x: 1, y: 2 });  // => "(1, 2)"

Derive Macros

Automatically implement common traits with #[derive(...)]. Works for types whose fields all implement the trait.

Common derivable traits
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct User {
    name: String,
    age: u32,
}

let u = User { name: "Alice".into(), age: 30 };
println!("{:?}", u);         // Debug
let u2 = u.clone();         // Clone
assert_eq!(u, u2);          // PartialEq
Copy + Clone for simple types
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point { x: f64, y: f64 }

let p1 = Point { x: 1.0, y: 2.0 };
let p2 = p1;       // Copy: p1 is still valid
println!("{:?}", p1);

Copy requires Clone. All fields must be Copy types (no String, Vec, etc.).

Default for struct initialization
#[derive(Debug, Default)]
struct Config {
    port: u16,        // defaults to 0
    verbose: bool,    // defaults to false
    name: String,     // defaults to ""
}

let config = Config { port: 8080, ..Default::default() };
println!("{:?}", config);

Trait Bounds

Constrain generic types to those implementing specific traits. No bound = no methods besides drop.

Single bound
fn print_it<T: std::fmt::Display>(item: T) {
    println!("{item}");
}
Multiple bounds with +
fn log_it<T: std::fmt::Display + std::fmt::Debug>(item: T) {
    println!("display: {item}, debug: {item:?}");
}
where clause (cleaner for complex bounds)
fn process<T, U>(t: &T, u: &U) -> String
where
    T: std::fmt::Display + Clone,
    U: Clone + std::fmt::Debug,
{
    format!("{t}")
}

Inline bounds and where clauses are equivalent. Use where when bounds get long.

impl Trait

Shorthand in argument position (like a generic). Hides the concrete type in return position.

Argument position: shorthand for generic
// These two are equivalent:
fn notify(item: &impl Summary) {
    println!("{}", item.summarize());
}

fn notify_generic<T: Summary>(item: &T) {
    println!("{}", item.summarize());
}
Return position: hide concrete type
fn make_iter() -> impl Iterator<Item = i32> {
    vec![1, 2, 3].into_iter()
}

for x in make_iter() {
    println!("{x}");
}
Return position: must be one concrete type
// ERROR: two different concrete types
// fn make_iter(asc: bool) -> impl Iterator<Item = i32> {
//     if asc { vec![1,2,3].into_iter() }
//     else { vec![3,2,1].into_iter().rev() }  // different type!
// }

// FIX: use Box<dyn Iterator>
fn make_iter(asc: bool) -> Box<dyn Iterator<Item = i32>> {
    if asc { Box::new(vec![1,2,3].into_iter()) }
    else { Box::new(vec![3,2,1].into_iter().rev()) }
}

Trait Objects (dyn Trait)

Dynamic dispatch via vtable. Use for heterogeneous collections or when the concrete type is not known at compile time.

Vec of trait objects
trait Shape {
    fn area(&self) -> f64;
}

struct Circle { radius: f64 }
impl Shape for Circle {
    fn area(&self) -> f64 { std::f64::consts::PI * self.radius.powi(2) }
}

struct Square { side: f64 }
impl Shape for Square {
    fn area(&self) -> f64 { self.side.powi(2) }
}

let shapes: Vec<Box<dyn Shape>> = vec![
    Box::new(Circle { radius: 1.0 }),
    Box::new(Square { side: 2.0 }),
];
dyn Trait as function parameter
fn print_area(shape: &dyn Shape) {
    println!("area: {:.2}", shape.area());
}
Static vs dynamic dispatch
// Static (generics): monomorphized, zero cost, larger binary
fn area_static(s: &impl Shape) -> f64 { s.area() }

// Dynamic (trait object): vtable, flexible, slight overhead
fn area_dynamic(s: &dyn Shape) -> f64 { s.area() }

Use generics when types are known at compile time. Use dyn Trait for heterogeneous collections.

Essential Std Traits: From, Into, Display, Debug

The most commonly implemented standard library traits. From/Into for conversion, Display/Debug for formatting.

From and Into
// Implementing From gives you Into for free
impl From<&str> for Username {
    fn from(s: &str) -> Self {
        Username(s.to_string())
    }
}

let u: Username = Username::from("alice");
let u: Username = "alice".into();  // Into is automatic
Display: user-facing format
impl std::fmt::Display for Username {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "@{}", self.0)
    }
}
println!("{}", username);  // => "@alice"
Debug: developer-facing format
#[derive(Debug)]
struct Config { port: u16, host: String }

let c = Config { port: 8080, host: "localhost".into() };
println!("{:?}", c);
// => Config { port: 8080, host: "localhost" }
println!("{c:#?}");  // pretty-printed

Associated Types vs Generic Parameters

Associated types: one implementation per type. Generic parameters: multiple implementations per type.

Associated type (Iterator pattern)
trait Iterator {
    type Item;                              // associated type
    fn next(&mut self) -> Option<Self::Item>;
}

// Each type implements Iterator once with one Item type
impl Iterator for Counter {
    type Item = u32;
    fn next(&mut self) -> Option<u32> { /* ... */ }
}
Generic parameter (Add pattern)
use std::ops::Add;

// A type can implement Add multiple times for different Rhs
impl Add<f64> for Point {
    type Output = Point;
    fn add(self, rhs: f64) -> Point { /* ... */ }
}

impl Add<Point> for Point {
    type Output = Point;
    fn add(self, rhs: Point) -> Point { /* ... */ }
}

Orphan Rule and Newtype Pattern

You cannot implement a foreign trait on a foreign type. The newtype pattern works around this.

Orphan rule violation
// ERROR (E0117): Display and Vec are both from std
// impl std::fmt::Display for Vec<i32> { ... }
Newtype pattern workaround
struct Wrapper(Vec<String>);

impl std::fmt::Display for Wrapper {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "[{}]", self.0.join(", "))
    }
}

let w = Wrapper(vec!["a".into(), "b".into()]);
println!("{w}");  // => "[a, b]"

The newtype IS yours, so you can implement any trait on it.

Turbofish and Disambiguation

The ::<> syntax helps the compiler when it cannot infer the type. UFCS disambiguates overlapping trait methods.

Turbofish on parse and collect
let n = "42".parse::<i32>().unwrap();
let v = vec![1, 2, 3].into_iter().collect::<Vec<_>>();
Fully qualified syntax (UFCS)
trait Pilot { fn fly(&self); }
trait Wizard { fn fly(&self); }

struct Human;
impl Pilot for Human { fn fly(&self) { println!("captain"); } }
impl Wizard for Human { fn fly(&self) { println!("up!"); } }

let h = Human;
Pilot::fly(&h);   // => "captain"
Wizard::fly(&h);  // => "up!"

// For associated functions (no self):
// <Type as Trait>::function()

Object Safety

A trait can only be used as dyn Trait if all methods have a receiver and none return Self.

Object-safe trait
trait Draw {
    fn draw(&self);        // has receiver (&self) -- OK
    fn name(&self) -> &str; // returns &str, not Self -- OK
}

let items: Vec<Box<dyn Draw>> = vec![/* ... */];
Not object-safe
trait Clonable {
    fn clone(&self) -> Self;  // returns Self -- NOT object-safe
}

// ERROR: cannot use dyn Clonable
// let items: Vec<Box<dyn Clonable>> = vec![];

Clone is not object-safe. Use generics instead, or restructure the trait.

Workaround: separate the non-safe method
trait Animal {
    fn speak(&self) -> &str;  // object-safe
}

// Clone goes on a separate trait or uses where Self: Sized
trait AnimalClone: Animal {
    fn clone_box(&self) -> Box<dyn Animal>
    where
        Self: Sized;
}
Learn Rust in Depth
Rust References & Lifetimes Practice →Rust Collections & Iterators Practice →
Warm-up1 / 2

Can you write this from memory?

Define a trait named `Greet` with a method `greet` that takes `&self` and returns nothing.

See Also
Iterators →Error Handling →Enums →

Start Practicing Rust

Free daily exercises with spaced repetition. No credit card required.

← Back to Rust Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.