Hello. I’m still somewhat of a beginner, so forgive me if this is a simple question, but I can attest that I’ve exhausted all sources I could find on the matter and am still not precisely sure how to tackle it.
I’ve been playing around with dynamic dispatch, and have ran into a bit of an odd dilemma.
While the lines…
…refuse to compile, throwing the error “self doesn't have a size known at compile-time".
I can roughly understand why - as I understand it, “self” in the trait declaration refers to the dynamically dispatched trait object, while “self” in the impl refers to the more concrete struct implementing the trait. However, (assuming I am understanding the inner workings correctly) is there any way around this? Ideally, I’d like to have a function that features the type signature of foo in my above example, but wouldn’t need to be manually implemented by anything implementing the trait.
Actually, Self does refer to whatever type implements the trait, and self to whatever the actual receiver is without any coercion to dyn. The problem is, rather, that if that type is some other unsized type — suppose there was impl BasicTrait for [u8] — it’s impossible to coerce Box<[u8]> into Box<dyn BasicTrait> because there’s no room to store two pieces of pointer metadata (the vtable and the slice length). So, you’re not allowed to write that method body because it wouldn’t compile in some cases — a default method body in a trait has to compile for all possible Self types.
For a solution, simpler than @Eliah-Lakhin’s but more limited, if you don't need foo() to be callable on Box<dyn BasicTrait> (as opposed to Box<BasicStruct>), then you can just give it bounds to exclude the cases that it won’t work for:
a slight variant of @Eliah-Lakhin 's approach is to use the standard From/Into instead of a custom SizedTrait for the blanket implementation.
// the dyn-dispatchable trait
trait Foo {
fn foo(&self);
fn bar(&mut self);
fn baz(self: Box<Self>);
}
// use `From` as a generic proxy for the unsize coercion
// generic type parameter has an implicit `Sized` bound,
// unless explicitly opted-out with `?Sized`
impl<'a, T: Foo + 'a> From<Box<T>> for Box<dyn Foo + 'a> {
fn from(value: Box<T>) -> Self {
value
}
}
// can accept both `Box<impl Foo>` and `Box<dyn Foo>`
fn generic_usage(foo: impl Into<Box<dyn Foo>>) {
let foo = foo.into();
//...
}
because Box is fundamental, this doesn't violate the orphan rule. also because of the implicit Sized bound, it doesn't conflict with the reflexive impl From<T> for T either.