[英]Define a method in trait returning a default implementation of Self
[英]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
作为返回类型也是如此,对吧? 它也不能存储在堆栈上,除非它的大小。
为什么第一个特征定义没有触发该错误?
( 这个问题是相关的,但它没有回答我的确切问题——除非我不明白。 )
这里发生了两组检查,这就是为什么差异看起来令人困惑的原因。
检查函数签名中的每种类型的有效性。 Option
本质上需要T: Sized
。 不需要Sized
返回类型很好:
trait Works { fn foo() -> Box<Self>; }
现有的答案很好地涵盖了这一点。
任何具有主体的函数也会检查所有参数是否为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))); }
它在错误消息中:
= note: required by `std::option::Option`
Option
需要调整类型的Sized
因为它在堆栈上分配。 默认情况下,具体类型定义的所有类型参数都绑定到Sized
。 某些类型选择使用?Sized
限制选择退出,但Option
没有。
为什么第一个特征定义没有触发该错误?
我认为这是一个有意识的设计决定,因为历史、面向未来和人体工程学。
首先,在 trait 定义中不会假设Self
是Sized
,因为人们会忘记写where Self: ?Sized
,而这些 trait 就没那么有用了。 默认情况下让 trait 尽可能灵活是一种明智的设计理念; 将错误推送到 impls 或让开发人员在需要的地方明确添加约束。
考虑到这一点,想象特质的定义不允许未分级的类型由方法返回。 每个返回Self
trait 方法还必须指定where Self: Sized
。 除了大量的视觉干扰之外,这对未来的语言开发也是不利的:如果将来允许返回未定义大小的类型(例如与placement-new一起使用),那么所有这些现有特征都将受到过度限制.
Option
的问题只是冰山一角,其他人已经解释了这一点; 我想在评论中详细说明您的问题:
有没有办法可以实现 fn
foo() -> Self
并且Self
没有被调整Sized
? 因为如果没有办法做到这一点,我不认为允许在没有Sized
限制的情况下返回Self
的意义。
由于两个问题,该方法确实使得(至少目前)无法将特征用作特征对象:
不能调用不带
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.