为什么在特征中返回 `Self` 有效,但返回 `Option<Self>` 需要 `Sized`?

Why does returning `Self` in trait work, but returning `Option<Self>` requires `Sized`?

这个特征定义编译得很好:

trait Works {
    fn foo() -> Self;
}

然而,这确实会导致错误:

trait Errors {
    fn foo() -> Option<Self>;
}
error[E0277]: the size for values of type `Self` cannot be known at compilation time
 --> src/lib.rs:6:5
  |
6 |     fn foo() -> Option<Self>;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `Self`
  = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
  = help: consider adding a `where Self: std::marker::Sized` bound
  = note: required by `std::option::Option`

使用 : Sized 超特性绑定,它有效。

我知道 traits 中的 Self 类型不会自动绑定为 Sized。而且我知道 Option<Self> 不能 returned(通过堆栈),除非它被调整大小(反过来,需要 Self 被调整大小)。但是,Self 和 return 类型一样,对吧?它也不能存储在堆栈上,除非它的大小。

为什么第一个特征定义没有触发该错误?

( 是相关的,但它没有回答我的确切问题——除非我不明白。)

在错误信息中:

= note: required by `std::option::Option`

Option 要求类型为 Sized 因为它在堆栈上分配。默认情况下,具体类型定义的所有类型参数都绑定到 Sized。有些类型选择退出 ?Sized 绑定,但 Option 不会。

Why doesn't the first trait definition already trigger that error?

考虑到历史、面向未来和人体工程学,我认为这是一个有意识的设计决定。

首先,Self在特征定义中不被假定为Sized,因为人们会忘记写where Self: ?Sized,而这些特征将不太有用。让特征尽可能灵活 默认 是一种明智的设计理念;将错误推送给 impl 或让开发人员在需要的地方显式添加约束。

考虑到这一点,假设特征定义允许方法返回未确定大小的类型。 returns Self 的每个 特征方法也必须指定 where Self: Sized。除了大量的视觉噪音之外,这对未来的语言发展是不利的:如果未来允许返回未确定大小的类型(例如与 placement-new 一起使用),那么所有这些现有特征都将是过于拘束。

这里进行了两组检查,这就是差异看起来令人困惑的原因。

  1. 检查函数签名中的每个类型的有效性。 Option 本质上需要 T: Sized。不需要 Sized 的 return 类型就可以:

    trait Works {
        fn foo() -> Box<Self>;
    }
    

    涵盖了这口井。

  2. 任何具有主体 的函数 还会检查所有参数是否为Sized。没有主体的特征函数没有应用此检查。

    为什么这有用?允许在 trait 方法中使用未确定大小的类型是允许 by-value trait objects 的关键部分,这是一个非常有用的功能。例如,FnOnce 不要求 SelfSized:

    pub trait FnOnce<Args> {
        type Output;
        extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
    }
    
    fn call_it(f: Box<dyn FnOnce() -> i32>) -> i32 {
        f()
    }
    
    fn main() {
        println!("{}", call_it(Box::new(|| 42)));
    }
    

非常感谢pnkfelix and nikomatsakis for answering my questions on this topic

Option的问题只是冰山一角,其他人已经解释过了;我想详细说明 :

is there a way I can implement fn foo() -> Self with Self not being Sized? Because if there is no way to do that, I don't see the point in allowing to return Self without a Sized bound.

由于 2 个问题,该方法确实使得(至少目前)无法将特征用作特征对象:

  1. Method has no receiver:

Methods that do not take a self parameter can't be called since there won't be a way to get a pointer to the method table for them.

  1. Method references the Self type in its arguments or return type:

这使得特征不是对象安全的,即不可能从中创建特征对象。

不过,您仍然可以将其用于其他用途:

trait Works {
    fn foo() -> Self;
}

#[derive(PartialEq, Debug)]
struct Foo;

impl Works for Foo {
    fn foo() -> Self {
        Foo
    }
}

fn main() {
    assert_eq!(Foo::foo(), Foo);
}