Function parameter: impl Trait vs &impl Trait

    fn print_item_impl1(item: impl Display) {
        println!("{}", item);
    }

    fn print_item_impl2(item: &impl Display) {
        println!("{}", item);
    }

    let p = Point(5, 10);
    print_item_impl1(&p);
    print_item_impl2(&p);

Hello,

Since print_item_impl1(&p) behave the same as print_item_impl2(&p).

So why do we add & to (item: impl Display) ?

Thanks,

For Display it doesn't matter if you pass T or &T, but that is not true for every trait. It is only true for Display, because it is implemented for &T where T: Display. Not every trait offers such an implementation.

Example:

struct Point(u8, u8);

trait MyDisplay {
    fn print(&self);
}

impl MyDisplay for Point {
    fn print(&self) {
        println!("({}, {})", self.0, self.1);
    }
}

// if you out-comment this, the code will fail to compile, because
// `MyDisplay` is not implemented for `&p` when calling `print_item_impl1(&p)`
impl MyDisplay for &Point {
    fn print(&self) {
        println!("({}, {})", self.0, self.1);
    }
}

fn print_item_impl1(item: impl MyDisplay) {
    item.print();
}

fn print_item_impl2(item: &impl MyDisplay) {
    item.print();
}

fn main() {
    let p = Point(5, 10);
    print_item_impl1(&p);
    print_item_impl2(&p);
}

Playground.

5 Likes

This is mostly a matter of taste in API design here. The reason for this is that Display is a trait that both: only has &self methods, and has a generic implementation for &T based in the one for T.

The argument for accepting impl Display would be that it's more general. It's more general, since passing any &impl Display still works due to the generic implementation mentioned above. Any you also noticed yourself tbst passing a reference still works.

The argument for accepting &impl Display is that it's

  • not any less general in a limiting way i.e. callers can just create a reference if they don't have one already
    • notably in some other cases this kind of difference can be relevant - e. g. if the value needs to be stored somewhere and the API would need to put any constraints on the lifetime of the reference
  • and accepting &impl Display by reference helps the caller of the API not forget that the function does not require an "owned value". With the impl Display version its possible to pass, say, a String by-value - then forget about the fact that that wasn't actually necessary, and on a later refractor that introduces additional usages of that String you might reasonably decide to add in a .clone()

I personally prefer the &impl Display choice here.

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.