如何在结构中分配impl特征



考虑一些不可访问但实现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>(可能更好。

最新更新