如何在结构体中指定一个实现了 Trait 的类型?

5
考虑一些不可访问但实现了API trait的结构体(HiddenInaccessibleStruct)。获得此隐藏类型的唯一方法是调用函数,该函数返回此类型的不透明实现。另一个结构体拥有某种类型,该类型使用此API trait。目前似乎无法在fn new()中分配此字段。以下代码也可以在Rust playgrounds找到:playgrounds
// -- 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特质Bound声明了一个返回Self的函数,因此它不是Sized。


你可能需要 box - prehistoricpenguin
1个回答

6
这里涉及到两个概念:通用类型和存在类型。一个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);
}

请看这个playground
这样做可以解决问题,但它只是把问题转移了:在main()中的abc的类型是Abc<impl Bound>,这是(目前)不可能写下的。如果你把那行改成let abc: () = ...,编译器会抱怨你试图将Abc<impl Bound>赋值给()。如果您试图遵循建议,并将该行更改为let abc: Abc<impl Bound> = ...,则编译器将抱怨此类型无效。因此,您必须使abc的类型隐含不表。这带来了一些使用上的问题,比如你不能轻松地将该类型的值放入其他结构体中等;基本上,存在类型“感染”了包含它的外部类型。 impl Trait类型大多数有用于即时使用,例如impl Iterator<Item=...>。在您的情况下,由于目标似乎是隐藏类型,您可以通过sealingBound来解决问题。在更一般的情况下,最好使用动态分派(Box<dyn Bound>)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接