Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does T not implement AsRef<T>?

Tags:

rust

This code does not compile:

fn ref_on_int<T>(_: T) where T: AsRef<i32> {}

fn main() {
    ref_on_int(&0_i32)
}

because

the trait bound `i32: std::convert::AsRef<i32>` is not satisfied

Why is it so?

This could be useful for example with a newtype like

struct MyInt(i32);

impl AsRef<i32> for MyInt {
    /* etc. */
}

then you could indifferently pass a reference on an i32 or a reference on a MyInt, because in the memory we have in both cases an i32.

like image 254
Boiethios Avatar asked Feb 20 '18 14:02

Boiethios


3 Answers

AsRef and Borrow are pretty similar at first glance, but they are used for different things. The Book describes the difference between them pretty well:

Choose Borrow when you want to abstract over different kinds of borrowing, or when you’re building a data structure that treats owned and borrowed values in equivalent ways, such as hashing and comparison.

Choose AsRef when you want to convert something to a reference directly, and you’re writing generic code.

In your case Borrow is a more reasonable choice because there is no conversion involved.

As for the question of why AsRef is not implemented between different integral types, I guess this would go against the intent of Rust to be expressive about casts; I think it's similar to the question Why can't I compare two integers of different types?.

like image 196
ljedrz Avatar answered Nov 18 '22 22:11

ljedrz


Here's an authoritative answer by Aaron Turon:

Borrow provides a blanket implementation T: Borrow<T>, which is essential for making the above collections work well. AsRef provides a different blanket implementation, basically &T: AsRef<U> whenever T: AsRef<U>, which is important for APIs like fs::open that can use a simpler and more flexible signature as a result. You can't have both blanket implementations due to coherence, so each trait is making the choice that's appropriate for its use case.

like image 21
matklad Avatar answered Nov 18 '22 22:11

matklad


I think that is one of the differences of AsRef and Borrow.

That is, Borrow<T> is implemented directly for &T, while AsRef<T> is not implemented for &T.

Funny thing is that AsRef<U> is implemented for &T if T implements AsRef<U>. That is, if you can use AsRef with a type, you can use it with a reference to the same time.

And another funny thing is that Borrow<T> is implemented for &T but also for T!

like image 2
rodrigo Avatar answered Nov 18 '22 22:11

rodrigo