Why not impl AsRef<T> for T

According to the documentation AsRef is a cheap reference-to-reference conversion:

Trait std::convert::AsRef

Used to do a cheap reference-to-reference conversion.

I wonder why there is no implementation like:

impl<T: ?Sized> AsRef<T> for T {
    fn as_ref(&self) -> &T {
        self
    }
}

(Playground)

I believe the only reason is that this conflicts with Rust's orphan rules and would require specialization, which isn't supported by Rust as of today.

But I think semantically, every &T should support an .as_ref() method, right? (Similar to every T supporting an .into() that converts it into type T.)

fn main() {
    let i: i32 = 5i32.into();
    // But this fails:
    let i: &i32 = (&5i32).as_ref();
}

(Playground)

If you want to abstract over "owned or borrowed form of T" generically, then that's Borrow. In the case of &T -> &T, it's not really a conversion.

2 Likes

Borrow isn't a "type" conversion in the narrower sense. It's a conversion between owned and borrowed forms of the "kinda-same type" (where "kinda-same type" means Eq, Ord, and Hash are consistent).

Hmmm, a matter of definition, I guess. We can "convert" from T into T through std::convert::Into::into, see Playground example above:

    let i: i32 = 5i32.into();

Also compare this:

With this:

Trait std::convert::Into

A value-to-value conversion that consumes the input value. […]

Both Into and AsRef describe conversions.

1 Like

IIRC It would conflict with other generic implementations - in particular this one I think: mod.rs - source

3 Likes

And currently also concrete ones like impl AsRef<str> for str. But that could be fixed, of course.


But technical reasons aside, should every T (from a semantical point of view) implement AsRef<T>? I feel like: yes.

2 Likes

You need intersection specialization. (I also suspect it would wreak havoc on inference in existing code; at least, PRs adding new AsRef implementations to std tend to do so.)

2 Likes

Some (preliminary) thoughts from another thread: