什么是'core :: types :: Sized`没有实现生锈中的'Self'类型?

Dou*_*oug 9 rust

这曾经工作:

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() {
}
Run Code Online (Sandbox Code Playgroud)

现在它出错了:

:15:21:15:25错误:该core::kinds::Sized类型未实现特征Self :15返回Foo :: new(self);

我可以猜出什么是错的; 它说我的Foo <'a,T>的impl是T,而不是大小?T,但我不是想存一个大小的?其中的元素; 我正在存储对Sized元素的引用.那应该是一个固定大小的指针.

我不知道我在做什么,或者为什么这是错的?

例如,我应该(我认为......)能够在我的Foo中存储一个&Array,没问题.我看不出有什么理由会强迫我的Foo实例被取消.

围栏链接:http://is.gd/eZSZYv

huo*_*uon 14

这里有两件事:特征对象强制(错误)和对象安全(修复它).

错误

正如错误消息所暗示的那样,代码的难点部分是Foo::new(self),这是因为pub fn new<T>(parent: &Array<T>) -> ...,self即被强制转换为&Array<T>特征对象.我将简化代码:

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

fn main() {}
Run Code Online (Sandbox Code Playgroud)

它给出了同样的东西:

<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
                     ^~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

Self是实现特征的类型的替代名称.与大多数通用参数不同,默认情况下Self可能是unsized(?Sized),因为RFC 546#20341允许例如impl Array<T> for Array<T>更频繁地默认工作(我们稍后会讨论).

变量self有类型&Self.如果Self是大小类型,那么这是一个普通的引用:一个指针.如果Self是未大小的类型(如[T]或类型),则&Self(&[T]&Trait)是切片/特征对象:胖指针.

出现错误是因为&T可以强制转换为特征对象的唯一引用T是大小时:Rust不支持使胖指针变胖,只有精细指针→胖指针有效.因此,因为编译器不知道Self将永远是Sized(记住,这是特殊的,?Sized默认情况下)它必须假定最坏的:即强制是不合法的,所以它的禁止.

解决它

我们正在寻找的解决方案似乎是合乎逻辑的,以确保Self: Sized在我们想要进行强制时.这样做的显而易见的方法是Self 始终 Sized,即覆盖默认?Sized界限,如下所示:

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

fn main() {}
Run Code Online (Sandbox Code Playgroud)

看起来不错!

除了它不起作用的小点; 但至少这是因为不同的原因,我们正在取得进展!特质对象只能由"对象安全"的特征组成(即可以安全地制作成特征对象),并且具有Sized Self破坏对象安全性的因素之一:

<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
                     ^~~~
Run Code Online (Sandbox Code Playgroud)

(我将该笔记的双重打印作为#20692提交.)

回到绘图板.解决方案还有其他一些"简单"的可能性:

  • 定义扩展特征trait ArrayExt: Sized + Array { fn as_foo(&self) { ... } }并为所有Sized + Array类型实现它
  • 只需使用免费功能 fn array_as_foo<A: Array>(x: &A) { ... }

但是,这些并不一定适用于每个用例,例如,特定类型无法通过重载默认方法来自定义行为.但是,幸运的是有一个修复!

Turon Trick

(以发现它的Aaron Turon命名.)

使用通用where子句,我们可以高度具体地说明何时Self应该实现Sized,将其限制为只需要它的方法,而不会感染其余的特征:

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

fn main() {}
Run Code Online (Sandbox Code Playgroud)

编译得很好!通过使用该where条款这样,编译器理解(一)强制是合法的,因为SelfSized如此self的薄指针,以及(b),该方法是非法的,反正特质对象调用,因此不破对象安全.要看到它被禁止,改变主体as_foo,以

let x = self as &Array; // coerce to a trait object
x.as_foo();
Run Code Online (Sandbox Code Playgroud)

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

正如所料.

将它全部包装起来

对原始的未简化代码进行此更改就像将该where子句添加到as_foo方法一样简单:

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() {
}
Run Code Online (Sandbox Code Playgroud)

编译没有错误.(注意:我必须删除不必要的内容<T>,pub fn new<T>因为这会导致推理失败.)

(我有一些正在进行的博客文章,包括特质对象,对象安全和Turon技巧,它们将在不久的将来出现在/ r/rust:第一个.)