为什么在trait方法声明中允许没有大小的类型?



为什么在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>语法一起使用,但在约束和特征对象中仍然有效。

相关内容

最新更新