(&mut MyStruct).into() for From<&MyStruct> defined in module

I have something like the following:

mod my_struct {
  pub struct MyStruct { ... }
  impl From<&crate::other_mod::OtherStruct> for Vec<MyStruct> { ... }
}
mod app {
  fn convert(convertee: &crate::other_mod::OtherStruct) -> Vec<my_struct::MyStruct> { 
    convertee.into()
  }
  fn convert_mut_direct(convertee: &mut crate::other_mod::OtherStruct) -> Vec<my_struct::MyStruct> { 
    convertee.into()
  }
  fn convert_mut_indirect(convertee: &mut crate::other_mod::OtherStruct) -> Vec<my_struct::MyStruct> { 
    convert(convertee)
  }
}

For some reason convert_mut_direct() fails but convert_mut_indirect() works just fine. What am I missing and where can I read more about this? An admittedly cursory glance at the books Mutability chapter didn't make it clear where/when I need to care about mut for a non mutable call.

The .into() call relies on the generic trait Into<T> and thus takes the &mut OtherStruct exactly as is. &mut OtherStruct does not impl Into<Vec<MyStruct>> and so Rust fails to find any matching method to call.

When you call convert(convertee), you pass a unique reference to a function that expects a shared reference. In this case, Rust employs a coercion called "auto-reborrow". If effectively turns the call into convert(&*convertee). Inside convert(), the convertee has the right type so that Rust can find its Into<Vec<MyStruct>> impl and the code compiles.

1 Like

Thank you!
Doing (&*convertee).into() made the mut thing work. I need to read more about the auto-reborrow.

1 Like

It's unfortunately not particularly easy to find some official documentation on it. The best I could find is this page of the 'nomicon, where it is referred to as "pointer-weakening".

There is one kind of reborrow, however, that is not covered by this: The automatic reborrow of &'a mut T to &'b mut T, where 'b is shorter than 'a. This reborrow makes the following possible without an error claiming "use of moved value: mr":

struct Foo;
fn takes_mut_ref(_: &mut Foo) {}
fn main() {
    let mut foo = Foo;
    let mr = &mut foo;
    takes_mut_ref(mr);
    takes_mut_ref(mr);
}

Unique references cannot be copied (hence "unique"), so you would expect that the first call moves mr and marks the variable as no longer usable. However, the call actually desugars to takes_mut_ref(&mut *mr), and it is fresh, shorter reference that gets moved. Hence, mr is still usable at the second function call.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.