Menu
Coddy logo textTech

Trait Bounds Syntax

Part of the Object Oriented Programming section of Coddy's Rust journey — lesson 40 of 61.

So far, you've learned to define traits and implement them for structs. You've also worked with generics to write flexible code. Now it's time to combine these concepts—using traits to constrain what types a generic function can accept.

Consider a generic function that needs to call a specific method on its parameter. Without any constraints, Rust has no guarantee that the type T has that method:

fn print_info<T>(item: T) {
    println!("{}", item.summarize());  // Error! T might not have summarize()
}

The solution is a trait bound. By adding : TraitName after the generic parameter, you tell Rust that T must implement that trait:

trait Summary {
    fn summarize(&self) -> String;
}

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());  // Now Rust knows this method exists
}

The syntax <T: Summary> reads as "T is any type that implements Summary." Now the function only accepts types that have the required behavior. If you try to pass a type that doesn't implement Summary, the compiler will reject it with a clear error.

This pattern is powerful because it lets you write generic code that still has access to specific functionality. Your function remains flexible—it works with any type—but only types that provide the behavior you need.

challenge icon

Challenge

Easy

Let's build a notification system that uses trait bounds to ensure only properly formatted messages can be sent! You'll create a generic function that accepts any type implementing a specific trait, demonstrating how trait bounds constrain generic parameters.

You'll organize your code across two files:

  • notification.rs: Define a public Notify trait with a method called message that takes &self and returns a String. Then create two public structs that implement this trait:
    • Email — with a public subject field (String). Its message method should return Email: {subject}
    • SMS — with a public content field (String). Its message method should return SMS: {content}
    Finally, create a public generic function called send_notification that accepts any type T implementing the Notify trait. This function should print the result of calling message() on the item.
  • main.rs: Bring in your notification module and use the inputs provided to create both an Email and an SMS instance. Call send_notification with each one to demonstrate that your generic function works with any type that implements Notify.

The key insight here is that send_notification doesn't know the concrete type it receives—it only knows that the type can produce a message. The trait bound T: Notify guarantees this capability.

Your output should show both notifications being sent:

Email: {subject}
SMS: {content}

For example, with inputs Meeting Tomorrow and On my way!:

Email: Meeting Tomorrow
SMS: On my way!

You will receive two inputs: the email subject and the SMS content.

Cheat sheet

A trait bound constrains a generic type parameter to only accept types that implement a specific trait.

Syntax for trait bounds uses : TraitName after the generic parameter:

fn function_name<T: TraitName>(item: T) {
    // Can now call methods from TraitName on item
}

Example with a trait bound:

trait Summary {
    fn summarize(&self) -> String;
}

fn print_info<T: Summary>(item: T) {
    println!("{}", item.summarize());
}

The notation <T: Summary> means "T is any type that implements Summary." This allows the function to call trait methods while remaining generic.

Try it yourself

mod notification;

use notification::{Email, SMS, send_notification};

fn main() {
    // Read inputs
    let mut subject = String::new();
    std::io::stdin().read_line(&mut subject).expect("Failed to read line");
    let subject = subject.trim().to_string();
    
    let mut content = String::new();
    std::io::stdin().read_line(&mut content).expect("Failed to read line");
    let content = content.trim().to_string();
    
    // TODO: Create an Email instance with the subject
    
    // TODO: Create an SMS instance with the content
    
    // TODO: Call send_notification with the email
    
    // TODO: Call send_notification with the sms
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming