简体   繁体   English

如何在结构中分配 impl 特征?

[英]How to assign an impl trait in a struct?

Consider some struct (HiddenInaccessibleStruct) that is not accessible, but implements an API trait.考虑一些不可访问但实现 API 特征的结构 (HiddenInaccessibleStruct)。 The only way to obtain an object of this hidden type is by calling a function, that returns an opaque implementation of this type.获得这种隐藏类型的 object 的唯一方法是调用 function,它返回这种类型的不透明实现。 Another struct owns some type, that makes use of this API trait.另一个结构拥有某种类型,它利用了这个 API 特征。 Right now, it seems not possible to assign this field in fn new() .现在,似乎不可能在fn new()中分配这个字段。 The code below can also be found in rust playgrounds .下面的代码也可以在rust 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.
        }
    }    
}

Update The API trait Bound declares a function, that returns Self, hence it is not Sized.更新API trait Bound 声明了一个 function,它返回 Self,因此它不是 Sized。

There are two concepts in mid-air collision here: Universal types and existential types.这里的空中碰撞有两个概念:通用类型和存在类型。 An Abc<T> is a universal type and we, including Abc , can refer to whatever T actually is as T (simple as that). Abc<T>是一种通用类型,我们,包括Abc ,可以将任何T实际上称为T (就这么简单)。 impl Trait -types are Rust's closest approach to existential types, where we only promise that such a type exists, but we can't refer to it (there is no T which holds the solution). impl Trait -types 是 Rust 最接近存在类型的方法,我们只有 promise 存在这种类型,但我们不能引用它(没有T持有解决方案)。 This also means your constructor can't actually create a Abc<T> , because it can't decide what T is.这也意味着您的构造函数实际上无法创建Abc<T> ,因为它无法确定T是什么。 Also see this article .另见这篇文章

One solution is to kick the problem upstairs: Change the constructor to take a T from the outside, and pass the value into it:一种解决方案是将问题踢到楼上:将构造函数更改为从外部获取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);
}

See this playground .看到这个游乐场

This works, but it only shifts the problem: The type of abc in main() is Abc<impl Bound> , which is (currently) impossible to write down.这可行,但它只会转移问题: main()中的abc的类型是Abc<impl Bound> ,(目前)不可能写下来。 If you change the line to let abc: () =... , the compiler will complain that you are trying to assign Abc<impl Bound> to () .如果将行更改为let abc: () =... ,编译器会抱怨您试图将Abc<impl Bound>分配给() If you try to comply with the advice and change the line to let abc: Abc<impl Bound> =... , the compiler will complain that this type is invalid.如果您尝试遵守建议并将行更改为let abc: Abc<impl Bound> =... ,编译器将抱怨此类型无效。 So you have to leave the type of abc being implied.因此,您必须保留隐含的abc类型。 This brings some useability issues with Abc<impl Bound> , because you can't easily put values of that type into other structs etc.;这给Abc<impl Bound>带来了一些可用性问题,因为您不能轻易地将这种类型的值放入其他结构等; basically, the existential type "infects" the outer type containing it.基本上,存在类型“感染”了包含它的外部类型。

impl Trait -types are mostly useful for immediate consumption, eg impl Iterator<Item=...> . impl Trait -types 主要用于即时消费,例如impl Iterator<Item=...> In your case, with the aim apparently being to hide the type, you may get away withsealing Bound .在您的情况下,显然是为了隐藏类型,您可以通过密封Bound逃脱。 In a more general case, it may be better to use dynamic dispatch ( Box<dyn Bound> ).在更一般的情况下,使用动态调度( Box<dyn Bound> )可能会更好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM