Purpose of `impl for Foo` VS `impl for &Foo`?

I am wondering what semantic is adding the possibility of impl for reference vs value.

Why not putting all in impl for &Foo ?

I noticed that :

  • methods in impl for &Foo are available from both value and reference
  • methods in impl for Foo are available only from value

My current guess is that :

impl for &Foo for "public" methods (available from everywhere)

impl for Foo for "protected" methods (in constructor etc …)

When it is recommended I use impl for Foo vs impl for &Foo ?

Thanks

Implementing a trait for Foo implements it for the type Foo, implementing a trait &Foo implements it specifically for the reference, which a reference is a different type.

Implementing a trait on Foo allows the methods to take ownership of the struct. This allows you to perform actions that a reference couldn't do, such as destroying the struct, moving the struct onto a new thread, or transforming the struct into a different type, such as in the case with the Iterator trait's map function.

Note: You can implement a trait on a type and have methods that take a reference

trait MyTrait {
    fn ownership_function(self); // takes ownership of self
    fn borrow_function(&self); // takes a reference to self
    fn mut_borrow_function(&mut self); // takes a mutable reference to self
}

Generally you want to implement a trait on the type itself. It's also useful if you plan on using generics or trait objects. For example you would be able to create a vector of types that implement the MyTrait.

let _: Vec<Box<dyn MyTrait>> = ...;

impl Trait for Foo is the normal case. Each trait method can then decide whether it takes self or &self or &mut self, so they may be callable on Foo, &Foo and/or &mut Foo, as appropriate.

impl Trait for &Foo is useful in some special cases. For example, the std::io::Write trait has methods that take &mut self because in many cases they need to mutate the writer. But some types, like File, can implement these methods without needing exclusive mutable access. For these types, we have impls like impl Write for &File. Now you can write to a File through a shared reference as well as a mutable one.

Implementing a trait for a reference type can also be used just as a convenience. For example, it can let callers pass an argument to a generic function without explicitly dereferencing it. In this case, it just makes the code look cleaner; it doesn't add any new capabilities.

3 Likes

It’s also sometimes used to allow more efficient code. For example, most of Iterator‘s methods take self so that the adapters can tear apart the source iterator. To make iterators reusable, the standard library also defines an implementation for mutable references:

impl<I:Iterator> Iterator for &mut I { /* ... */ }
1 Like

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.