Remi D
Remi D

Reputation: 643

Why are Borrow and AsRef implemented differently?

The Borrow and AsRef traits are very similar in Rust. If I understand correctly, they have the exact same signature (except for the method name) when being implemented on the same type, the difference lies in how they are used.

But when you take a look at their implementation, for instance on Vec<T> there is a subtle difference:

slice.rs:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Borrow<[T]> for Vec<T> {
    fn borrow(&self) -> &[T] {
        &self[..]
    }
}

vec.rs:

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> AsRef<[T]> for Vec<T> {
    fn as_ref(&self) -> &[T] {
        self
    }
}

I believe the implementation of AsRef here works thanks to Deref coercion. But couldn't the same mechanism be used for the Borrow implementation?

Upvotes: 19

Views: 1771

Answers (2)

kmdreko
kmdreko

Reputation: 60447

There is no practical difference between the two implementations.

You are correct in your assessment of the semantic differences: the &self[..] body uses the index operator with an open range to create a slice of the entire vector, while the basic self body relies on Deref coercion where the compiler automatically converts &Vec<T> to &[T].

There is no functional difference. As you've noted, the method signatures are identical and thus are identical to the borrow checker outside the implementation. A comment above suggests an internal difference between borrowing the vector and borrowing a slice from that vector, but that is not the case; in both the slice's lifetime is derived from self. The generated assembly for both methods are also identical; although they use different ways to get the same result and optimizations are sometimes chaotic, I'd be surprised if there were any code-generation difference in any situation.

So the only difference is that of style. Both these implementations have existed as they are since before the Rust 1.0 release. As a comment above mentions, the Borrow implementation is older and while the Deref trait did exist at the time of its writing, it was only freshly implemented for Vecs (a month prior) and was not finalized until months later. So it is not surprising that the original Borrow implementation used the pre-deref syntax. The AsRef implementation did not exist until after Deref was finalized, so it is not surprising the author used it.

So the differences that you see are inconsequential and historical only.

Upvotes: 10

Robert Bradley
Robert Bradley

Reputation: 534

One site gives a really helpful answer:

We can see how they’re kind of the same: they both deal with owned and borrowed versions of some type. However, they’re a bit different.

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.

            —Source

Upvotes: 1

Related Questions