rustref

How does rust translate a mutable ref to immutable ref in generic traits?


I was trying to implement the From trait for a custom struct in order to build it from a reference to another struct:

struct Base { ...properties }

struct Derived { ...other_properties }

impl From<&Base> for Derived {
    fn from(value: &Base) -> Self {
        Self { ... }
    }
}

It was fine until I tried to call it inside a function with a mutable ref to a Base instance as argument:

fn foo(base: &mut Base) {
    ...do stuff on base
    let derived = Derived::from(base);
}

I thought the compiler could detect the refs have the same types and pass it as immutable instead of mutable, and it would be fine, but instead I had a compile error:

the trait bound Derived: From<&mut Base> is not satisfied the trait From<&Base> is implemented for Derived

I then tried to reproduce the issue with my own code, so i created a trait CustomFrom with a single function having the same signature as from from the From trait:

trait CustomFrom<T> {
    fn custom_from(base: T) -> Self;
}

And implemented it:

impl CustomFrom<&Base> for Derived {
    fn custom_from(value: &Base) -> Self {
        Self { ... }
    }
}

and called it the same way a called the original from in the first place:

fn foo(base: &mut Base) {
    ...do stuff on base
    let derived = Derived::from(base);
    let custom = Derived::custom_from(base);

}

Except this time the compiler was ok with my custom trait usage.

I know I can solve the issue by calling from using Derived::from(& *base);, but I would like to know:

What's the difference between the two traits?

Why was the compiler able to use the reference as immutable with my trait but not with the standard one?

Full Minimal Example:

struct Base {
    a: u8,
    b: u8,
    c: u8,
    d: u8,
    e: u8,
    f: u8,
}

trait CustomFrom<T> {
    fn custom_from(param: T) -> Self;
}

impl Base {
    fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> Self {
        Self { a, b, c, d, e, f }
    }
    fn a(&self) -> &u8 {
        &self.a
    }
    fn c(&self) -> &u8 {
        &self.c
    }
    fn d(&self) -> &u8 {
        &self.d
    }
}

struct Derived {
    a: u8,
    c: u8,
    d: u8,
}

impl From<&Base> for Derived {
    fn from(base: &Base) -> Self {
        Self {
            a: *base.a(),
            c: *base.c(),
            d: *base.d()
        }
    }
}

impl CustomFrom<&Base> for Derived {
    fn custom_from(base: &Base) -> Self {
        Self {
            a: *base.a(),
            c: *base.c(),
            d: *base.d()
        }
    }
}

fn main() {
    let mut base = Base::new(1, 2, 3, 4, 5, 6);
    let ex = Derived::from(&base);
}

fn foo(base: &mut Base) {
    let test1 = Derived::from(base);
    let test2 = Derived::custom_from(base);
}

fn bar(mut base: Base) {
    let test1 = Derived::from(&base);
    let test2 = Derived::custom_from(&base);
}

Solution

  • Your CustomFrom replication appears to work because the compiler can make additional deductions if a trait has only one implementation. If you introduce another implementation like impl CustomFrom<()> for Derived, then it also will not work with &mut Base:

    error[E0277]: the trait bound `Derived: CustomFrom<&mut Base>` is not satisfied
      --> src/main.rs:72:17
       |
    72 |     let test2 = Derived::custom_from(base);
       |                 ^^^^^^^ the trait `CustomFrom<&mut Base>` is not implemented for `Derived`
       |
       = help: the following other types implement trait `CustomFrom<T>`:
                 <Derived as CustomFrom<&Base>>
                 <Derived as CustomFrom<()>>
    

    playground link

    Beyond that scenario, the compiler generally doesn't do any coercions (like &mut T to &T) to find a suitable trait implementation.