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 traitFrom<&Base>
is implemented forDerived
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);
}
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<()>>
Beyond that scenario, the compiler generally doesn't do any coercions (like &mut T
to &T
) to find a suitable trait implementation.