为什么在特征中返回`Self`起作用,但是返回`Option <self>`却需要`Sized`?

时间:2019-01-31 16:46:59

标签: rust traits

此特征定义可以很好地编译:

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超性就可以了。

我知道trait中的Self类型不会自动绑定为Sized。而且我知道Option<Self>不能返回(通过堆栈),除非它被调整大小(这反过来需要调整Self的大小)。但是,对于Self来说,返回类型也一样,对吗?除非已调整大小,否则也无法将其存储在堆栈中。

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

This question是相关的,但是它不能回答我的确切问题–除非我不理解。

3 个答案:

答案 0 :(得分:8)

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

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

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

    existing answer涵盖了这一点。

  2. 任何带有主体的函数 还会检查所有参数是否为Sized。没有身体的特质功能不应用此检查。

    这为什么有用?好吧,目前目前不可能在稳定的Rust中使用,但是允许在特征方法中使用大小不一的类型是允许按值特征对象的关键步骤,一个备受期待的功能。例如,FnOnce不需要SelfSized

    pub trait FnOnce<Args> {
        type Output;
        extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
    }
    
    #![feature(unsized_locals)]
    
    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

答案 1 :(得分:6)

它在错误消息中:

  

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

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

  

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

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

首先,特质定义中不假定SelfSized,因为人们会忘记写where Self: ?Sized,而这些特质将没有太大用处。在默认情况下,让特征尽可能地灵活是一种明智的设计理念;推送错误以暗示或使开发人员在需要的地方明确添加约束。

请牢记这一点,想象一下特征定义没有允许通过方法返回大小不一的类型。返回Self每个特征方法也必须指定where Self: Sized。除了带来很多视觉干扰之外,这对于将来的语言开发也将是不利的:如果将来允许返回未确定大小的类型(例如,与placement-new一起使用),那么所有这些现有特征将是过度约束。

答案 2 :(得分:1)

Option的问题只是冰山一角,其他人已经对此进行了解释;我想阐述your question in the comment

  

有没有办法我可以用foo() -> Self来实现fn Self   Sized?因为如果没有办法做到这一点,我没有看到在点   允许返回Self,而不受Sized的限制。

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

  1. Method has no receiver
  

不带参数self的方法不能被调用,因为那里   将不会是一个的方式来获得一个指向方法表他们。

  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);
}
相关问题