Menu
Coddy logo textTech

Returning Types with Traits

Part of the Object Oriented Programming section of Coddy's Rust journey. Lesson 43 of 61.

You've used trait bounds to constrain what types a function can accept. But what about what a function returns? The impl Trait syntax lets you specify that a function returns "some type that implements this trait" without naming the concrete type.

Here's the syntax in action:

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

struct Article {
    headline: String,
}

impl Summary for Article {
    fn summarize(&self) -> String {
        self.headline.clone()
    }
}

fn create_summary() -> impl Summary {
    Article { headline: String::from("Breaking News!") }
}

The return type impl Summary tells callers "you'll get something that implements Summary." The caller can use any method from the trait, but doesn't need to know the actual type is Article.

This is particularly useful when the concrete type is complex or when you want to hide implementation details. The function promises a capability (the trait), not a specific type.

However, there's an important limitation: the function must return exactly one concrete type. You cannot conditionally return different types that implement the same trait. That requires trait objects, which you'll learn about later.

// This works - always returns Article
fn make_item() -> impl Summary {
    Article { headline: String::from("Hello") }
}

// This would NOT compile - two different types
// fn make_item(flag: bool) -> impl Summary {
//     if flag { Article { ... } } else { Tweet { ... } }
// }
challenge icon

Challenge

Easy

Let's build a greeting card factory that uses the impl Trait return type to hide implementation details! You'll create a function that returns "something that can greet" without revealing the concrete type to the caller.

You'll organize your code across two files:

  • greetings.rs: Define a public Greet trait with a greet method that takes &self and returns a String. Then create a Card struct (it doesn't need to be public!) with a message field (String). Implement the Greet trait for Card, returning the message. Finally, create a public function called create_greeting that takes a String parameter and returns impl Greet. This function should create and return a Card with the given message.
  • main.rs: Bring in your greetings module and use the input provided to call create_greeting. The beauty here is that your main file doesn't know about the Card type at all. It only knows it received something that implements Greet. Call the greet method on the returned value and print the result.

The key insight is that Card stays private to the module, but callers can still use it through the trait interface. The function promises a capability (greeting), not a specific type.

Your output should display the greeting:

{message}

For example, with input Happy Birthday!:

Happy Birthday!

And with input Congratulations on your promotion!:

Congratulations on your promotion!

You will receive one input: the greeting message.

Try it yourself

mod greetings;

use greetings::Greet;

fn main() {
    // Read the greeting message from input
    let mut input = String::new();
    std::io::stdin().read_line(&mut input).expect("Failed to read line");
    let message = input.trim().to_string();

    // TODO: Call create_greeting with the message
    // Note: You don't know the concrete type - only that it implements Greet!
    
    // TODO: Call the greet method and print the result
}
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

Practice on your own: Online Rust compiler