Reputation: 6855
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).
Upvotes: 4
Views: 457
Reputation: 58835
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.
Upvotes: 4
Reputation: 19672
&Box<S>
is not directly equal to Box<&S>
, and this is why it does not compile directly.
You can relatively easily fix this by dereferencing, like so:
use std::ops::Deref;
trait T{}
struct S{}
impl T for S{}
fn f1(s : &S) -> &(dyn T) {
s
}
fn f2(s : &Box<S>) -> &(dyn T) {
s.deref()
}
(The trait Deref
is there for slightly easier readability)
The call to deref()
operates over &self
, so having &Box<S>
is sufficient to call it. It simply returns &S
, and since that implements T
the types check out.
Upvotes: 1