rust 中的 'core::kinds::Sized` is not implemented for the type `Self' 是什么?

What is 'core::kinds::Sized` is not implemented for the type `Self' in 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() {
}

现在错误:

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

我能猜出哪里出了问题;它是说我的 Foo<'a, T> 是针对 T,而不是 Sized 的? T,但我不是要存储 Sized?其中的元素;我将 reference 存储到其中的 Sized 元素。那应该是一个指针,固定大小。

我不明白我正在做的事情有什么问题,或者为什么不对?

例如,我应该(我认为...)能够在我的 Foo 中存储一个 &Array,没问题。我看不出有任何理由会强制我的 Foo 实例缩小大小。

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

这里发生了两件事:特征对象强制转换(错误)和对象安全(修复)。

错误

如错误消息所示,代码的困难部分是 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() {}

给出相同的东西:

<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 是实现特征的类型的替代名称。与大多数通用参数不同,Self 默认情况下可能未调整大小 (?Sized),因为 RFC 546 and #20341 允许例如impl Array<T> for Array<T> 默认情况下更频繁地工作(我们稍后会谈到)。

变量 self 的类型为 &Self。如果 Self 是大小类型,那么这是一个普通引用:一个指针。如果 Self 是未确定大小的类型(如 [T] 或特征),则 &Self&[T]&Trait)是 slice/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() {}

看起来不错!

除了有一点它不起作用;但至少这是出于不同的原因,我们正在取得进展!特征对象只能由 "object safe" 的特征组成(即可以安全地制成特征对象),并且具有 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
                     ^~~~

(我将笔记的双重打印归档为#20692。)

回到绘图板。还有其他一些 "easy" 可能的解决方案:

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

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

Turon 把戏

(以发现它的 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() {}

这编译得很好!通过像这样使用 where 子句,编译器理解 (a) 强制转换是合法的,因为 SelfSized 所以 self 是一个瘦指针,并且 (b)无论如何调用特征对象的方法都是非法的,因此不会破坏对象安全。要查看它是否被禁止,请将 as_foo 的正文更改为

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

给予

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

符合预期。

总结一下

对原始未简化代码进行此更改就像将 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() {
}

编译无误。 (注意。我不得不删除 pub fn new<T> 中不必要的 <T>,因为那会导致推理失败。)

(我有一些正在进行的博客文章,涉及特征对象、对象安全和 Turon 技巧,它们将出现在 /r/rust in the near future: first one 上。)