Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is 'core::kinds::Sized` is not implemented for the type `Self' in rust?

Tags:

rust

This used to work:

struct Foo<'a, T> {
  parent:&'a (Array<T> + 'a)
}

impl<'a, T> Foo<'a, T> { //'
  pub fn new<T>(parent:&Array<T>) -> Foo<T> {
    return Foo {
      parent: parent
    };
  }
}

trait Array<T> {
  fn as_foo(&self) -> Foo<T> {
    return Foo::new(self);
  }
}

fn main() {
}

Now it errors:

:15:21: 15:25 error: the trait core::kinds::Sized is not implemented for the type Self :15 return Foo::new(self);

I can kind of guess what's wrong; it's saying that my impl of Foo<'a, T> is for T, not Sized? T, but I'm not trying to store a Sized? element in it; I'm storing a reference to a Sized element in it. That should be a pointer, fixed size.

I don't see what's wrong with what I'm doing, or why it's wrong?

For example, I should (I think...) be able to store a &Array in my Foo, no problem. I can't see any reason this would force my Foo instance to be unsized.

playpen link: http://is.gd/eZSZYv

like image 830
Doug Avatar asked Jan 07 '15 09:01

Doug


People also ask

What is sized in Rust?

In Rust a type is sized if its size in bytes can be determined at compile-time. Determining a type's size is important for being able to allocate enough space for instances of that type on the stack. Sized types can be passed around by value or by reference.

What is a trait object?

A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.


1 Answers

There's two things going on here: trait objects coercions (the error), and object safety (fixing it).

The error

As suggested by the error message, the difficult part of the code is the Foo::new(self), and this is because pub fn new<T>(parent: &Array<T>) -> ..., that is, self is being coerced to an &Array<T> trait object. I'll simplify the code to:

trait Array {
  fn as_foo(&self) {
    let _ = self as &Array; // coerce to a trait object
  }
}

fn main() {}

which gives the same thing:

<anon>:3:13: 3:27 error: the trait `core::kinds::Sized` is not implemented for the type `Self`
<anon>:3     let _ = self as &Array; // coerce to a trait object
                     ^~~~~~~~~~~~~~

Self is the stand-in name for the type that implements the trait. Unlike most generic parameters, Self is possibly-unsized (?Sized) by default, since RFC 546 and #20341 for the purposes of allowing e.g. impl Array<T> for Array<T> to work by default more often (we'll come to this later).

The variable self has type &Self. If Self is a sized type, then this is a normal reference: a single pointer. If Self is an unsized type (like [T] or a trait), then &Self (&[T] or &Trait) is a slice/trait object: a fat pointer.

The error appears because the only references &T that can be cast to a trait object are when T is sized: Rust doesn't support making fat pointers fatter, only thin pointer → fat pointer is valid. Hence, since the compiler doesn't know that Self will always be Sized (remember, it's special and ?Sized by default) it has to assume the worst: that the coercion is not legal, and so it's disallowed.

Fixing it

It seems logical that the fix we're looking for is to ensure that Self: Sized when we want to do a coercion. The obvious way to do this would be to make Self always Sized, that is, override the default ?Sized bound as follows:

trait Array: Sized {
  fn as_foo(&self) {
    let _ = self as &Array; // coerce to a trait object
  }
}

fn main() {}

Looks good!

Except there's the small point that it doesn't work; but at least it's for a difference reason, we're making progress! Trait objects can only be made out of traits that are "object safe" (i.e. safe to be made into a trait object), and having Sized Self is one of the things that breaks object safety:

<anon>:3:13: 3:17 error: cannot convert to a trait object because trait `Array` is not object-safe [E0038]
<anon>:3     let _ = self as &Array; // coerce to a trait object
                     ^~~~
<anon>:3:13: 3:17 note: the trait cannot require that `Self : Sized`
<anon>:3     let _ = self as &Array; // coerce to a trait object
                     ^~~~
<anon>:3:13: 3:17 note: the trait cannot require that `Self : Sized`
<anon>:3     let _ = self as &Array; // coerce to a trait object
                     ^~~~

(I filed the double printing of the note as #20692.)

Back to the drawing board. There's a few other "easy" possibilities for a solution:

  • define an extension trait trait ArrayExt: Sized + Array { fn as_foo(&self) { ... } } and implement it for all Sized + Array types
  • just use a free function fn array_as_foo<A: Array>(x: &A) { ... }

However, these don't necessarily work for every use case, e.g. specific types can't customise the behaviour by overloading the default method. However, fortunately there is a fix!

The Turon Trick

(Named for Aaron Turon, who discovered it.)

Using generalised where clauses we can be highly specific about when Self should implement Sized, restricting it to just the method(s) where it is required, without infecting the rest of the trait:

trait Array {
  fn as_foo(&self) where Self: Sized {
    let _ = self as &Array; // coerce to a trait object
  }
}

fn main() {}

This compiles just fine! By using the where clause like this, the compiler understands that (a) the coercion is legal because Self is Sized so self is a thin pointer, and (b) that the method is illegal to call on a trait object anyway, and so doesn't break object safety. To see it being disallowed, changing the body of as_foo to

let x = self as &Array; // coerce to a trait object
x.as_foo();

gives

<anon>:4:7: 4:15 error: the trait `core::kinds::Sized` is not implemented for the type `Array`
<anon>:4     x.as_foo();
               ^~~~~~~~

as expected.

Wrapping it all up

Making this change to the original unsimplified code is as simple adding that where clause to the as_foo method:

struct Foo<'a, T> { //'
  parent:&'a (Array<T> + 'a)
}

impl<'a, T> Foo<'a, T> {
  pub fn new(parent:&Array<T>) -> Foo<T> {
    return Foo {
      parent: parent
    };
  }
}

trait Array<T> {
  fn as_foo(&self) -> Foo<T> where Self: Sized {
    return Foo::new(self);
  }
}

fn main() {
}

which compiles without error. (NB. I had to remove the unnecessary <T> in pub fn new<T> because that was causing inference failures.)

(I have some in-progress blog posts that go into trait objects, object safety and the Turon trick, they will appear on /r/rust in the near future: first one.)

like image 198
huon Avatar answered Oct 18 '22 21:10

huon