繁体   English   中英

为什么在 trait 中返回 `Self` 有效,但返回 `Option<Self> ` 需要 `Size`?

[英]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 supertrait 绑定,它可以工作。

我知道 Trait 中的Self类型不会自动绑定到Sized 而且我知道Option<Self>不能返回(通过堆栈),除非它被调整(反过来,这需要Self被调整)。 但是, Self作为返回类型也是如此,对吧? 它也不能存储在堆栈上,除非它的大小。

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

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

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

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

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

    现有的答案很好地涵盖了这一点。

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

    为什么这很有用? 允许在 trait 方法中使用 unsized 类型是允许按值 trait objects的关键部分,这是一个非常有用的特性。 例如, FnOnce不需要Self被调整Sized

     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 和 nikomatsakis 回答我关于这个主题的问题

它在错误消息中:

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

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

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

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

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

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

Option的问题只是冰山一角,其他人已经解释了这一点; 我想在评论中详细说明您的问题

有没有办法可以实现 fn foo() -> Self并且Self没有被调整Sized 因为如果没有办法做到这一点,我不认为允许在没有Sized限制的情况下返回Self的意义。

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

  1. 方法没有接收器

不能调用不带self参数的方法,因为没有办法为它们获取指向方法表的指针。

  1. 方法在其参数或返回类型中引用 Self 类型

这使得 trait 不是对象安全的,即不可能从它创建一个 trait 对象。

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

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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM