Box Borrow Implementation

Hi all,

I've been learning on rust smart pointers and collections, and there is a question stucked in my mind. Why Rust' Box, Rc, Arc don't implement Borrow for its internal type like this:

impl<T, Q: ?Sized> Borrow<Q> for Box<T> 
where 
    T: Borrow<Q>
{
    ...
}

For context, I'm working with HashMap<Rc<String>,i32>. And I want to do something like this:

let m: HashMap<Rc<String>,i32> = ...;
let k: &str = "some_sring";
m.get(k); // this fails

It seems the m.get(k) is failing because Rc<String> doesn't implement Brrow<str>, and it only implements Borrow<&String>. So I need to convert my &str to String in order to use it. I'm ok with this limitation. But, I want to know why does this is the case, and I want to know if I'm missing something.

This isn't what you're asking, but why not use Rc<str> instead of Rc<String>? This has less indirection and solves the borrow problem.

They would conflict with

impl<T: ?Sized> Borrow<T> for T {}

And also Box is #[fundamental], which means you can implement traits for Box<LocalType> like you can for LocalType, so adding it for Box specifically would be a breaking change.

I think the conflict is legit without considering #[fundamental] anyway though... yep, it is.

Interesting, I didn't know we can use Rc like this. This seems better, thank you.

Yeah, I noticed this, and it makes sense.
But, I spend quite some time thinking why we ca't do something like this:impl Borrow<Q> for Box<T> where T != Q, T: Borrow<Q> and make it non-conflicting with impl Borrow<T> for Box<T>.

Negative reasoning with regards to coherence is so far intentionally limited in Rust, as it makes things much more fragile in the general case: suddenly implementing a new trait for a concrete type is a breaking change. But there is an interest in expanding coherence. Progress seems slow however.

Here's an open RFC for != specifically, with the difficulty of assigning meaning to != mentioned in passing here.

It still would be breaking for Box due to the #[fundamental] thing though.


If you want to read way more about negative reasoning and coherence more generally, there's

Though really, the problem is only with blanket implementations. Various covered implementations for combinations of std types could be added, which I guess is what this issue is about.

Many combinations of the existing std Borrow implementations would be variations on the impl Borrow<UnsizedThing> for Box<SizedThing> though, to which the answer is probably "just use Box<UnsizedThing> instead", as with your Rc<str> case.