为什么在trait方法声明中允许没有大小的类型?例如,以下代码编译为:
trait Blah {
fn blah(&self, input: [u8]) -> dyn Display;
}
但是实现Blah
是不可能的:
impl Blah for Foo {
fn blah(&self, input: [u8]) -> dyn Display {
"".to_string()
}
}
// error[E0277]: the size for values of type `(dyn std::fmt::Display + 'static)` cannot be known at compilation time
// error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
给blah
一个默认实现也是不可能的:
trait Blah {
fn blah(&self, input: dyn Display) -> dyn Display { "".to_string() }
}
// error[E0277]: the size for values of type `(dyn std::fmt::Display + 'static)` cannot be known at compilation time
也不允许嵌套的不确定大小的类型。这种不一致使我认为这是一个编译器错误:
trait Blah {
fn blah(&self, input: [str]) -> dyn Display;
}
// error[E0277]: the size for values of type `str` cannot be known at compilation time
我发现了一些旧的GitHub问题,声称这种行为是故意的,但我找不到原因。为什么这是有意的行为?如果实现这种性质的trait是不可能的,为什么编译器不在trait声明中捕获它?
我认为这里有两个不同的问题:未设置大小的函数参数和未设置大小的返回类型。
未调整大小的参数类型
unsize函数参数实际上在feature(unsized_fn_params)
下的夜间Rust中实现和可用。这很可能会在某个时候稳定下来,然后就有可能实现像这样的特性:
trait Crab {
fn pinch(&self, data: str);
}
目前在稳定版Rust中是无法实现的。
实际上,标准库使用该特性为Box<dyn FnOnce()>
实现FnOnce()
,这需要将*self
移动到call_once
方法中。因为FnOnce
是(某种程度上)外部可见的¹,而std
总是允许使用不稳定的特性,如果不允许trait函数拥有它们(稳定的),可能没有任何方法来实现unsized_fn_params
(在夜间)。然而,这只是猜测。
参见如何在Rust中按值传递一个盒装trait对象?
无大小返回类型
顾名思义,unsized_fn_params
只影响参数;即使在unsized_locals
(更广泛、更宽松、更不完整的特性)下,也不允许没有大小的返回值。因此,允许-> dyn Display
存在的理由远没有那么令人信服,而且有一个很好的案例可以证明这是一个实际的bug,或者至少值得关注。
我相信"故意的"事实上,这部分只适用于参数类型,包括返回类型是一个意外。
¹Fn
特征是特殊的,因为在稳定中它们只能与Fn(...)
语法一起使用,而不能与Fn<Args>
语法一起使用,但在约束和特征对象中仍然有效。