为什么在trait中返回`self`,但返回`Option <Self>`需要`Sized`?

Luk*_*odt 13 traits rust

这个特征定义编译好:

trait Works {
    fn foo() -> Self;
}
Run Code Online (Sandbox Code Playgroud)

但是,这会导致错误:

trait Errors {
    fn foo() -> Option<Self>;
}
Run Code Online (Sandbox Code Playgroud)
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`
Run Code Online (Sandbox Code Playgroud)

: Sized超级界限下,它有效.

我知道特征中的Self类型不会自动绑定Sized.而且我理解Option<Self>不能返回(通过堆栈),除非它的大小(反过来,需要Self调整大小).但是,Self返回类型也一样,对吧?除非它的大小,否则它也不能存储在堆栈中.

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

(这个问题是相关的,但它没有回答我的确切问题 - 除非我不理解它.)

She*_*ter 8

这里发生了两组检查,这就是差异看起来令人困惑的原因.

  1. 检查函数签名中的每种类型的有效性.Option本质上要求T: Sized.不需要的返回类型Sized很好:

    trait Works {
        fn foo() -> Box<Self>;
    }
    
    Run Code Online (Sandbox Code Playgroud)

    现有的回答涵盖了这口井.

  2. 具有正文的任何函数也会检查所有参数Sized.没有身体的特征功能没有应用此检查.

    为什么这有用?好吧,目前无法在稳定的Rust中使用它,但允许在特征方法中使用未经过类型化的类型是允许按值特征对象的关键一步,这是一个备受期待的特性.例如,FnOnce不要求SelfSized:

    pub trait FnOnce<Args> {
        type Output;
        extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
    }
    
    Run Code Online (Sandbox Code Playgroud)
    #![feature(unsized_locals)]
    
    fn call_it(f: Box<dyn FnOnce() -> i32>) -> i32 {
        f()
    }
    
    fn main() {
        println!("{}", call_it(Box::new(|| 42)));
    }
    
    Run Code Online (Sandbox Code Playgroud)

非常感谢pnkfelix和nikomatsakis回答我关于这个主题的问题.

  • 顺便说一句,`Box <dyn FnOnce()>`现在每晚工作([例](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9b00c85886a8f8447f8763d577697d16)).见[#54183](https://github.com/rust-lang/rust/pull/54183). (2认同)

Pet*_*all 6

它出现在错误消息中:

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

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

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

由于历史,面向未来和人体工程学,我认为这是一个有意识的设计决策.

首先,Self不要假设它们Sized处于特征定义中,因为人们会忘记写作where Self: ?Sized,而这些特征就不那么有用了.让特质在默认情况下尽可能灵活是一种明智的设计理念; 推送错误推动或使开发人员明确地在需要的地方添加约束.

考虑到这一点,想象特质的定义并没有允许未分级的类型由方法返回.返回的每个特征方法Self都必须指定where Self: Sized.除了存在很多视觉噪声之外,这对于未来的语言开发也是不利的:如果将来允许返回未经过类型化的类型(例如,与placement-new一起使用),那么所有这些现有特征都将受到过度限制.