Hey all I am trying to create a reference of a box and I am facing some issue.
Here is a minimal reproducible example
trait A {
fn smth(&self);
}
struct B {}
impl A for B {
fn smth(&self) {}
}
fn f(_: &Box<dyn A>) {}
fn main() {
f(&Box::new( B {}));
}
error[E0308]: mismatched types
--> src/test.rs:16:7
|
16 | f(&Box::new( B {}));
| ^^^^^^^^^^^^^^^^ expected trait object `dyn A`, found struct `B`
|
= note: expected reference `&Box<(dyn A + 'static)>`
found reference `&Box<B>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `belote_libre_backend` due to previous error
warning: build failed, waiting for other jobs to finish...
error: build failed
What is wrong in my code ?
More generally how am I supposed to create a reference to a box object ?
You'd need a cast from Box<B> to Box<dyn A>. So you could do something like: &(Box::new(B {}) as Box<dyn A>) or leave it upto type inference by doing &(Box::new(B {}) as _).
As always in Rust, explicit is better than implicit, and that's why you need the explicit cast.
Could you elaborate? I’m not seeing the target of & … expressions listed as coercion sites, whereas “final line of a block” is listed, which is – I suppose – why &{ … } makes a difference.
The coercion site is the function call argument, and coercion site propagation is not required, as one of the listed coercions is
TyCtor(T) to TyCtor(U), where TyCtor(T) is one of
...
&T
and where U can be obtained by T by unsized coercion
And unsized coercion applies to Box<_>. Moreover, if this was a propagation issue, parenthetical subexpressions are listed. (There are also a number of other issues around coercion with blocks acting differently, though most of them seem to be about Deref coercion, e.g. 23014,26978,57749.)
You can’t coerce &Box<B> into &Box<dyn A>, in case that’s what you’re suggesting. (Let me read the remainder of your answer in more detail though…)
Ah, so you’re saying, the reference suggest that coercing &Box<B> into &Box<dyn A> should be possible (while it’s of course fundamentally impossible in practice)?
Because Box<B> coerces into Box<dyn A> by modifying the box, turning it from a thing pointer to a fat pointer. Now, you can’t modify the box behind a second layer of indirection anymore which is why
&B -> &dyn A> Box<B> -> Box<dyn A>
work, but any of
&&B -> &&dyn A> Box<&B> -> Box<&dyn A> * &Box<B> -> &Box<dyn A> Box<Box<B>> -> Box<Box<dyn A>> *
fundamentally can’t work in practice, i.e. it’s impossible to write the assembly for any sound function implementing this coercion.
* Well… in these cases with Box<…> in the outer layer, it’s at least impossible without re-allocation.
If that field has type Bar<T>, then Bar<T> implements Unsized<Bar<U>>
T is not part of the type of any other fields
Additionally, a type Foo<T> can implement CoerceUnsized<Foo<U>> when T implements Unsize<U>or CoerceUnsized<Foo<U>>. This allows it to provied an unsized coercion to Foo<U>.
I think, the reference is weird about it’s wording what constitutes an “unsized coercion”. There’s the unsizing and the coercion, and the section about it mixes up the two a bit, in particular in the last section
Additionally, a type Foo<T> can implement CoerceUnsized<Foo<U>> when T implements Unsize<U> or CoerceUnsized<Foo<U>>. This allows it to provide a unsized coercion to Foo<U>.
In practice, the two concepts Foo: Unsize<Bar> and Foo: CoerceUnsized<Bar> are distinct, and only the latter is an actual coercion, while the former is an important consideration for determining whether or not certain CoerceUnsized implementations are met.
Is kinda weird, because it seems to merely try to list the CoerceUnsize in the standard library while being awefully incomplete. I.e. this section appears to be a natural language description of this list of impls
impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b T
where
'b: 'a,
T: Unsize<U> + ?Sized,
U: ?Sized,
impl<'a, T, U> CoerceUnsized<&'a mut U> for &'a mut T
where
T: Unsize<U> + ?Sized,
U: ?Sized,
impl<T, U> CoerceUnsized<*const U> for *const T
where
T: Unsize<U> + ?Sized,
U: ?Sized,
impl<T, U> CoerceUnsized<*mut U> for *mut T
where
T: Unsize<U> + ?Sized,
U: ?Sized
impl<T, U, A> CoerceUnsized<Box<U, A>> for Box<T, A>
where
T: Unsize<U> + ?Sized,
A: Allocator,
U: ?Sized,
Do the different impls have any practical impact? I guess they might actually -- I'm now reminded of how unsized coercion can apply variance to the dyn lifetime even in invariant position. You can always copy a &_ out of an invariant position to get variance back, but you can't copy a &mut out. I'm not sure if that's it or not, but perhaps.
The most important aspect are the additional impls. To name one example, there’s an impl for Pin that, translated into the style of that bulled point, would mean that the infinite list of cases
Pin<&T>>
Pin<&mut T>
Pin<*const T>
Pin<*mut T>
Pin<Box<T>>
Pin<Pin<&T>>>
Pin<Pin<&mut T>>
Pin<Pin<*const T>>
Pin<Pin<*mut T>>
Pin<Pin<Box<T>>>
Pin<Pin<Pin<&T>>>>
…
would need to be included
fn demonstration(x: Pin<Pin<Pin<&B>>>) -> Pin<Pin<Pin<&dyn A>>> { x }
Ah, alright. Haven’t tested what practical impact those might have, but it reminded me of the dyn Trait + 'lifetime lifetimes, too, where e.g. Cell<Box<dyn Trait + 'lifetime>> can be coerced to a different lifetime.