Impl From on reference without repetition?

If I want to implement From for a struct as well as for a &struct, is there a way to do that without needing to repeat things (not a macro - though I guess if there's no other choice...)?

Example playground:

struct Foo {}
struct Bar {}

impl From<Foo> for Bar {
    fn from(_:Foo) -> Self {
        Self {}
    }
}

fn main() {
    //works
    let foo = Foo{};
    let _bar:Bar = foo.into();
    
    //fails
    let foo = Foo{};
    let _bar:Bar = (&foo).into();
}

The problem is that Rust sees Struct and &Struct as two different types. Accordingly, you need two different impl's. In addition, the example you provide is a special one as it does not use the argument. If we do it another way you see that two different implementations are mandatory:

#[derive(Clone)]
struct Foo {}
struct Bar(Foo);

impl From<Foo> for Bar {
    fn from(foo: Foo) -> Self {
        Bar(foo)
    }
}

impl From<&Foo> for Bar {
    fn from(foo: &Foo) -> Self {
        Bar(foo.clone()) // requires the clone
    }
}
1 Like

I don't think they are always mandatory to be different... e.g. if I just want to use the same as_slice() method on the struct in both cases (and let's say that method isn't from a Trait, it's on the struct itself)

They are definitely different types, however Rust has convince features that sometimes let you call reference methods on owned types. E.g. you can call as_bytes on a String even though it was defined on &str. You can look up the Deref trait which is what let's you do this.

Note that this does not make String the same type as &str, and you can only go one way: from owned to reference unless the type is copy.

2 Likes

Aren't &String and &str different types?

Tried playing around with Deref to allow calling a reference method on owned type but couldn't get it to work yet... still tinkering though :slight_smile:

Giving it an explicit AsRef seems to work... is this the idiomatic approach?

Playground

use std::convert::AsRef;

struct Foo {}
struct Bar {}

impl <T: AsRef<Foo>> From<T> for Bar {
    fn from(_:T) -> Self {
        Self {}
    }
}

impl AsRef<Foo> for Foo {
    fn as_ref(&self) -> &Foo {
        self
    }
}

fn main() {
    let foo = Foo{};
    let _bar:Bar = foo.into();
    
    let foo = Foo{};
    let _bar:Bar = (&foo).into();
}

Correct. You don't need Deref to call methods defined for &String (e.g. a &self method on String), but you do need it for methods on &str.

2 Likes