首页 > 解决方案 > 当特征具有关联类型时如何将特征实现存储在一起

问题描述

当我们想要将多个类型一起存储在一个集合中时,可以使用特征对象。

但是当特征具有关联类型时,我不知道该怎么做。

trait Error {}

trait Trait {
    type Error: Error;
    fn set(&mut self, key: String, value: String) -> Result<(), Self::Error>;
}

struct StructA;
impl Trait for StructA {
    type Error = ErrorA;
}

enum ErrorA {}
impl Error for ErrorA {}

struct StructB;
impl Trait for StructB {
    type Error = ErrorB;
}

enum ErrorB {}
impl Error for ErrorB {}

fn main() -> Result<(), Box<dyn Error>> {
    let value: Box<dyn Trait<Error = dyn Error>> = match 0 {
        0 => Box::new(StructA),
        _ => Box::new(StructB),
    };

    value.set(String::from("key"), String::from("value"))?;

    Ok(())
}

我必须在Box<dyn Trait<Error = _>>这里指定关联的类型,但我不知道哪种类型适合。我试过dyn Error了,但它不起作用。

标签: genericstypesrusttraitsassociated-types

解决方案


如果可以更改Trait,则可以通过返回动态错误类型来使其对所有错误类型都是对象安全的,例如将签名更改为set()

fn set(&mut self, key: String, value: String) -> Result<(), Box<dyn Error + '_>>;

但是,如果您无法更改Trait以使其对象安全,您仍然可以创建自己的对象安全特征,并为实现的每种类型提供一揽子实现Trait。例如:

trait DynamicTrait {
    fn set(&mut self, key: String, value: String) -> Result<(), Box<dyn Error + '_>>;
}

impl<T: Trait> DynamicTrait for T {
    fn set(&mut self, key: String, value: String) -> Result<(), Box<dyn Error + '_>> {
        Trait::set(self, key, value).map_err(|e| Box::new(e) as _)
    }
}

DynamicTrait工作方式与 完全相同Trait,但Box<dyn Error>在出错时返回,因此它是对象安全的。例如,这只是在不修改任何一个Trait或其实现的实现的情况下工作StructA,并且StructB

fn main() {
    let value: Box<dyn DynamicTrait> = match 0 {
        0 => Box::new(StructA),
        _ => Box::new(StructB),
    };
}

操场


推荐阅读