此特征定义可以很好地编译:
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是相关的,但是它不能回答我的确切问题–除非我不理解。)
答案 0 :(得分:8)
这里发生了两组检查,这就是为什么差异看起来令人困惑的原因。
检查功能签名中的每种类型的有效性。 Option
本质上要求T: Sized
。不需要Sized
的返回类型很好:
trait Works {
fn foo() -> Box<Self>;
}
existing answer涵盖了这一点。
任何带有主体的函数 还会检查所有参数是否为Sized
。没有身体的特质功能不应用此检查。
这为什么有用?好吧,目前目前不可能在稳定的Rust中使用,但是允许在特征方法中使用大小不一的类型是允许按值特征对象的关键步骤,一个备受期待的功能。例如,FnOnce
不需要Self
为Sized
:
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
没有选择退出。
为什么第一个特征定义尚未触发该错误?
由于历史,面向未来和人机工程学的原因,我认为这是一个有意识的设计决策。
首先,特质定义中不假定Self
为Sized
,因为人们会忘记写where Self: ?Sized
,而这些特质将没有太大用处。在默认情况下,让特征尽可能地灵活是一种明智的设计理念;推送错误以暗示或使开发人员在需要的地方明确添加约束。
请牢记这一点,想象一下特征定义没有不允许通过方法返回大小不一的类型。返回Self
的每个特征方法也必须指定where Self: Sized
。除了带来很多视觉干扰之外,这对于将来的语言开发也将是不利的:如果将来允许返回未确定大小的类型(例如,与placement-new一起使用),那么所有这些现有特征将是过度约束。
答案 2 :(得分:1)
Option
的问题只是冰山一角,其他人已经对此进行了解释;我想阐述your question in the comment:
有没有办法我可以用
foo() -> Self
来实现fnSelf
Sized
?因为如果没有办法做到这一点,我没有看到在点 允许返回Self
,而不受Sized
的限制。
由于以下两个问题,该方法确实使得(至少目前)无法将特征用作特征对象:
不带参数
self
的方法不能被调用,因为那里 将不会是一个的方式来获得一个指向方法表他们。
这会导致特征不是对象安全的,即无法从中创建特征对象。
尽管如此,您仍然可以完美地将其用于其他用途:
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);
}