Question about implementing traits for mutable references


#1

I’m running into a compilation error when working with a trait implemented for a mutable reference and I’m confused why a particular change to the code fixes the error.

I originally ran into this when working with std::io::Read and serde, but I was able to reproduce the problem with more generic code. Here’s that code (playground)

trait TestTrait {
    fn consume(&mut self);
}

struct Wrapper {
    buf: Vec<u8>,
}

impl TestTrait for Wrapper {
    fn consume(&mut self) {
        self.buf.drain(0..);
    }
}

impl<'a, T: TestTrait> TestTrait for &'a mut T {
   fn consume(&mut self) {
       drop(self);
    }
}

fn main() {
    let data: Vec<u8> = vec![0xc0, 0xff, 0xee];
    
    let mut wrapper = Wrapper{ buf: data };
    
    handle(&mut wrapper);
}

fn handle(wrapper: &mut Wrapper) {
    helper(wrapper);
    
    println!("{:?}", wrapper.buf);
}

fn helper<T: TestTrait>(obj: T) {
    drop(obj);
}

The error produced is:

error[E0382]: use of moved value: `wrapper.buf`
  --> <anon>:33:22
   |
31 |     helper(wrapper);
   |            ------- value moved here
32 |     
33 |     println!("{:?}", wrapper.buf);
   |                      ^^^^^^^^^^^ value used here after move
   |
   = note: move occurs because `wrapper` has type `&mut Wrapper`, which does not implement the `Copy` trait

If I change the helper function to:

fn helper<T: TestTrait>(obj: &mut T)

compilation succeeds.

This is where I get confused. Here’s what I think is going on:

  • In the helper(wrapper) call, wrapper has the effective type of TestTrait before the change and after the change, wrapper has the effective type of &mut TestTrait
  • Since wrapper is a &mut TestTrait after the change, the compiler can give ownership of wrapper to helper and then assign ownership of wrapper back to handle after helper returns.

If my thinking on what’s going with code is correct, what rules allow the compiler to implicitly return ownership of the wrapper struct back to the caller after helper completes?

Also, when should one implement a trait for a mutable reference of the trait? Is there a pattern that is enabled by doing this?

Thanks for any comments/insights!


#2

I don’t know the details but &mut *wrapper always fixes it when I see this.


#3
impl<'a, T: TestTrait> TestTrait for &'a mut T {
   fn consume(&mut self) {
       drop(self);
    }
}

I assume this should have been call to consume and not drop. The drop doesn’t do anything useful.

fn main() {
    let data: Vec<u8> = vec![0xc0, 0xff, 0xee];
    
    let mut wrapper = Wrapper{ buf: data };
    
    handle(&mut wrapper);
}

main owns data up until the point it is moved into Wrapper.
main owns wrapper to the end. handle takes a borrow of it.

fn helper<T: TestTrait>(obj: T) {
    drop(obj);
}

A call to helper moves obj into it and drops it. It would be dropped anyway at end of function.

fn helper<T: TestTrait>(obj: &mut T) {
    drop(obj);
}

Borrows instead of moving. The drop is of no use.

renamed to h_wrapper to avoid confusion;

fn handle(h_wrapper: &mut Wrapper) {
    helper(h_wrapper);
    
    println!("{:?}", h_wrapper.buf);
}

wrapper exists throughout the function call but in main. h_wrapper is the borrow.
The original helper moves h_wrapper so it can no longer be used in remainder of function, hence the error message.