Rust: Can't cast `self as &Trait` in trait default method

Created on 11 Oct 2018  Â·  4Comments  Â·  Source: rust-lang/rust

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;
    …
}

Workaround 2

trait Blah {
    fn test(&self, this: &Blah) {
        …
    }
}

Workaround 3

Manually construct the reference, somehow.

A-traits T-compiler

Most helpful comment

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).

All 4 comments

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 Self is concrete. Self may not be sized, but &Self certainly 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).

Was this page helpful?
0 / 5 - 0 ratings