Purpose of generic trait implementations?

After learning a lot from this question: How does File::open(...) take either a &str or Path type?

I was looking at one of the generic implementations of the AsRef<T> trait [source].

impl<T: ?Sized, U: ?Sized> AsRef<U> for &T
where
    T: AsRef<U>,
{
    fn as_ref(&self) -> &U {
        <T as AsRef<U>>::as_ref(*self)
    }
}

I was wondering what the purpose of the generic implementation was? Are concrete implementations instantiated whenever fn <T as AsRef<U>>::as_ref is used? So then the purpose of the generic implementation is to provide the implementation behavior for types that don't have to manually generate the implementation of AsRef<U> on itself?

1 Like

This particular one is so that no matter how many references deep you are, as_ref will give you the "base" item -- sort of how like you can access a struct field with . through multiple layers of references. E.g. this works:

    let s = String::new();
    let _: &str = (&&&&&&&&&&s).as_ref();

Because String: AsRef<str>, so &String: AsRef<str>, so &&String: AsRef<str>, ...

On a more general level, yes, blanket implementations like this provide the implementation for the entire set of types that meet the bounds, and they're instantiated on demand, similar to how generic functions are monomorphized on demand.

2 Likes

I think of blanket implementations like this as a conditional or implication in the logical language of the trait system:

For any lifetime 'a, and any type T, Sized or not, and any type U, Sized or not,
if T: AsRef<U>,
then &'a T: AsRef<U> (and the implementation of the as_ref method is as follows: […])

Or to take another example from the standard library, this:

impl<'a, B> Borrow<B> for Cow<'a, B> where
    B: ToOwned + ?Sized,
    <B as ToOwned>::Owned: 'a, 

becomes:

For any lifetime 'a and any type B, Sized or not,
if B: ToOwned and <B as ToOwned>::Owned: 'a,
then Cow<'a, B>: Borrow<B> (and the implementation of the borrow method is as follows: […])

Of course some blanket implementations don't have an "if", they apply to any type/lifetime without qualifications, like this one.

There's an ongoing project to re-implement the trait system using a (limited) logic programming language, based on thinking about trait impls in these terms.

1 Like

Thanks! This makes a lot of sense, especially within the context of the standard library.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.