考虑一些不可访问但实现API特征的结构(HiddenAccessibleStruct(。获得这种隐藏类型的对象的唯一方法是调用一个函数,该函数返回这种类型的不透明实现。另一个结构拥有某个类型,该类型利用了这个API特性。现在,似乎不可能在fn new()
中分配此字段。下面的代码也可以在铁锈色的操场上找到。
// -- public api
trait Bound {
fn call(&self) -> Self;
}
// this is not visible
#[derive(Default)]
struct HiddenInaccessibleStruct;
impl Bound for HiddenInaccessibleStruct {
fn call(&self) -> Self { }
}
// -- public api
pub fn load() -> impl Bound {
HiddenInaccessibleStruct::default()
}
struct Abc<T> where T : Bound {
field : T
}
impl<T> Abc<T> where T : Bound {
pub fn new() -> Self {
let field = load();
Abc {
field // this won't work, since `field` has an opaque type.
}
}
}
更新API特征绑定声明了一个函数,该函数返回Self,因此它没有大小。
在半空中碰撞中有两个概念:普遍类型和存在类型。Abc<T>
是一种通用类型,我们,包括Abc
,可以将T
实际是什么称为T
(就这么简单(。impl Trait
-类型是Rust最接近存在类型的方法,我们只承诺存在这样的类型,但我们不能引用它(没有T
可以提供解决方案(。这也意味着你的构造函数实际上不能创建一个Abc<T>
,因为它不能决定什么是T
。
一种解决方案是将问题升级:更改构造函数以从外部获取T
,并将值传递给它:
impl<T> Abc<T>
where
T: Bound,
{
pub fn new(field: T) -> Self {
Abc { field }
}
}
fn main() {
let field = load();
let abc = Abc::new(field);
}
看看这个操场。
这是有效的,但它只是转移了问题:main()
中abc
的类型是Abc<impl Bound>
,(目前(无法写下来。如果将行更改为let abc: () = ...
,编译器将抱怨您试图将Abc<impl Bound>
分配给()
。如果您试图遵守建议并将行更改为let abc: Abc<impl Bound> = ...
,编译器将抱怨此类型无效。因此,您必须保留abc
的类型是隐含的。这给Abc<impl Bound>
带来了一些可使用性问题,因为您无法轻松地将该类型的值放入其他结构等中。;基本上是存在类型";感染";包含它的外部类型。
impl Trait
类型主要用于即时消费,例如impl Iterator<Item=...>
。在您的情况下,目的显然是隐藏类型,您可以密封Bound
。在更一般的情况下,使用动态调度(Box<dyn Bound>
(可能更好。