Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't a boxed struct be borrowed as a trait?

Tags:

rust

traits

Given a struct S implementing a trait T, why doesn't Box<S> implement Borrow<dyn T>?

The following code, that I would have expected to compile, doesn't:

trait T{}
struct S{}
impl T for S{}

fn f1(s: &S) -> &dyn T {
    s
}

fn f2(s: &Box<S>) -> &dyn T {
    std::borrow::Borrow::borrow(s)
}

Why does f1 compile while f2 doesn't? (The conversion from &S to &dyn T is done in the first case and not in the second).

like image 571
lovasoa Avatar asked Sep 18 '25 05:09

lovasoa


1 Answers

This is to do with the way that type inference and type coercion work. The Borrow<B> trait's parameter is the type of the borrowed value, and the type checker needs to know what it is.

If you just write:

std::borrow::Borrow::borrow(s)

Then the type B in Borrow<B> will be inferred from the surrounding code. In your case it is inferred to be dyn T because that's the return value. However, dyn T is a completely different type from S, so it doesn't type-check.

Once the type checker knows that the value being returned is of type &S then it can coerce it to a &dyn T, but you need to give it that information:

fn f2(s: &Box<S>) -> &dyn T {
    let s: &S = std::borrow::Borrow::borrow(s);
    s
}

Or, more concisely:

fn f2(s: &Box<S>) -> &dyn T {
    std::borrow::Borrow::<S>::borrow(s)
}

The reason why Sébastien Renauld's answer works is because Deref uses an associated type instead of a type parameter. The type-checker can easily infer the <S as Deref>::Target because there can only be one implementation of Deref per type and the associated Target type is uniquely determined. Borrow is different because Box<S> could implement Borrow<()>, Borrow<i32>, Borrow<Box<Option<Vec<bool>>>>,... so you have to be more explicit about which implementation you intend.

like image 51
Peter Hall Avatar answered Sep 20 '25 22:09

Peter Hall