需要Sized特性的特质与无法拥有该特质的特质对象有什么关系?

14

我有这段代码(playground):

trait NodeLike: Sized {}

fn main() {
    let s: Box<NodeLike> = panic!();
}

哪个无法编译:

error[E0038]: the trait `NodeLike` cannot be made into an object
 --> src/main.rs:4:12
  |
4 |     let s: Box<NodeLike> = panic!();
  |            ^^^^^^^^^^^^^ the trait `NodeLike` cannot be made into an object
  |
  = note: the trait cannot require that `Self : Sized`

尽管我读了所有的内容,但我仍然不明白为什么它没有约束Sized也能编译通过。

据我所知:

  • Box<NodeLike>被视为Box<dyn NodeLike>,它对方法调用使用动态分派。
  • Box<NodeLike>无论其项类型如何,都被视为大小已确定。
  • 大小/非大小理论是必要的,因为有些类型的大小无法预先知道 (例如数组或字符串)。
  • 特征上的Sized标记强制实现类型具有大小。

要求实现类型是Sized与不能拥有该特征对象(使用动态分派的对象)有什么关系?


你应该使用 dyn 表示法。这是一种不太含糊的表示法。 - Boiethios
所有内容都在文档中解释。 - Boiethios
文档没有解释得很清楚。 为什么?我们不能创建类型为Box<Foo>或&Foo的对象,因为在这种情况下Self不会有大小。 - Henning
@trentcl 那个链接很有帮助,谢谢。所以 Sized 标记基本上只是一个开关,用于启用或禁用给定特质的对象实例? - Henning
1
作为超级特质,这是它所做的一件事。但通常情况下,“Sized”只是一个由编译时已知大小的类型满足的特质。您可以将其用作超级特质来禁用子特质的特质对象,但这不是其主要用途。 - trent
1个回答

9

Self: ?Sized添加到特质类型本身上是特质对象所需的属性,即用于“对象安全”,尽管您可以在Self: ?Sized特质上具有Sized类型的impl。这导致了混淆。

这是在RFC 255中决定的缺点,该文档涉及对象安全(警告:过时的 Rust 语法)。

这是一篇长篇阅读,但其中一个替代方法是仅通过分析特质的方法来确定“对象安全性”。RFC承认,这种限制将使得一些本应该能工作的代码无法编译。(“这是一项破坏性变更,并禁止了今天合法的一些安全代码。”)

如果我们仅将限制降低到实际需要它的特质成员函数,例如这样编译:

trait NodeLike {
    fn method_foo(&self) -> Self
    where
        Self: Sized;
}

fn main() {
    let s: Box<NodeLike> = panic!();
    // Compiles!
}

然而,我们无法通过特质对象调用那些Self:Sized方法,这是一个已经在其他地方解释过的限制。在这里,调用s.method_foo();将导致编译错误。
请注意,即使该方法根本不使用Self并且本来可以成为可调用的特质对象方法,Self:Sized的约束也会限制编译。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接