trait Blah {
fn test(&self) {
self as &Blah;
}
}
``
error[E0277]: the size for values of typeSelfcannot be known at compilation time
--> src/lib.rs:3:9
|
3 | self as &Blah;
| ^^^^ doesn't have a size known at compile-time
|
= help: the traitstd::marker::Sizedis not implemented forSelf
= note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-sized>
= help: consider adding awhere Self: std::marker::Sizedbound
= note: required for the cast to the object typedyn Blah`
[(playground)](https://play.rust-lang.org/?gist=68b51b8defb6bddf8d1c0b6ec1eafa2d&version=stable&mode=debug&edition=2015)
Well, `self` is usually a thin pointer, tho it may be a fat pointer if eg it's a slice. However `Self` is concrete. `Self` may not be sized, but `&Self` certainly is. `mem::size_of` is a const fn, so [this code](https://play.rust-lang.org/?gist=ba7372f0d844c49ea3f4b338180f67d3&version=stable&mode=debug&edition=2015) proves that the size is known at compile time! Well, it maybe a matter of precisely when in the compile time. Anyways, that code should totally compile.
## Workaround 1
```rust
trait Blah {
fn as_blah(&self) -> &Blah;
…
}
trait Blah {
fn test(&self, this: &Blah) {
…
}
}
Manually construct the reference, somehow.
self is Type Self not trait Blah
&Blah is &dyn Blah
trait Blah {
fn test(&self) {
let a : &Self= &self;
}
}
here
struct A;
impl Blah for A{
}
let a = A;
a.test(); // Self == A
However
Selfis concrete.Selfmay not be sized, but&Selfcertainly is.
This is irrelevant; this is an "unsizing coercion," which only applies to types behind a pointer (as defined by the CoerceUnsized trait), and the thing behind a pointer here is Self.
Indeed, as you say, &Self is sized, but this simply means that &&Self (not &Self) can be coerced to &Trait.
I do not believe there are any object-safety or soundness reasons why this coercion cannot be done. (The cast could just copy the existing vtable for &Trait; but currently, it does not. Also, there is the question of &Subtrait, which cannot just copy or compute an offset into their vtable, but technically can construct a new one)
may be
trait Blah {
fn test(&self) {
let a : &Blah = self as &Blah;
^ here can not generate vtable because we don't know Real type of Self
}
fn test2(& self) -> u8;
}
different Self must has different vtable ? in most of case
That's not quite it though, because you can add where Self: Sized to make the default definition compile:
trait Blah {
fn test(&self) where Self: Sized {
let a : &Blah = self as &Blah;
}
}
But of course, this is a lame workaround, because it prevents you from calling the method on &dyn Trait.
A legitimate example of why the Sized bound is needed is for e.g. Self = [u8]. You cannot cast &[u8] to &dyn Trait because there would be nowhere to store the length of the slice (&[u8] stores it in the same place where &dyn Trait puts its vtable).
Most helpful comment
That's not quite it though, because you can add
where Self: Sizedto make the default definition compile:But of course, this is a lame workaround, because it prevents you from calling the method on
&dyn Trait.A legitimate example of why the
Sizedbound is needed is for e.g.Self = [u8]. You cannot cast&[u8]to&dyn Traitbecause there would be nowhere to store the length of the slice (&[u8]stores it in the same place where&dyn Traitputs its vtable).