当特征具有关联类型时如何将特征实现存储在一起

问题描述

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

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

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 但它不起作用。

解决方法

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

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

但是,如果您不能改变 Trait 以使其对象安全,您仍然可以创建自己的对象安全特性,并为每个类型提供一个全面的实现实现 Trait。例如:

trait DynamicTrait {
    fn set(&mut self,Box<dyn Error + '_>>;
}

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

DynamicTraitTrait 完全一样,但在出错时返回 Box<dyn Error>,因此它是对象安全的。例如,这无需修改 Trait 或其实现 StructAStructB 的实现:

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

Playground

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...