Generics 在Rust中没有trait对象的情况下,如何在trait的多个实现者之间进行选择?

Generics 在Rust中没有trait对象的情况下,如何在trait的多个实现者之间进行选择?,generics,rust,Generics,Rust,我有一个名为Dataset的特性,它有一个单一的方法来通过文件名和相关的错误类型检索文件内容。该特性是针对不同的文件系统结构实现的,例如用于基于目录的访问的PathBuf,或用于在压缩zip存档中访问的ZipArchive。出于测试目的,还存在一个用于HashMap的实现 trait数据集{ 类型错误:错误+'静态; fn read(&mut self,name:&str)->结果; } 现在,我想根据给定的文件名来推断要使用的实现:如果路径是一个目录,一个zip存档,如果扩展名是“代码> .

我有一个名为
Dataset
的特性,它有一个单一的方法来通过文件名和相关的
错误
类型检索文件内容。该特性是针对不同的文件系统结构实现的,例如用于基于目录的访问的
PathBuf
,或用于在压缩zip存档中访问的
ZipArchive
。出于测试目的,还存在一个用于
HashMap
的实现

trait数据集{
类型错误:错误+'静态;
fn read(&mut self,name:&str)->结果;
}

现在,我想根据给定的文件名来推断要使用的实现:如果路径是一个目录,一个zip存档,如果扩展名是“代码> .zip 或<代码> .bzip ,等等。考虑一个函数<代码> fnRun(DataSet:IMPL DataSet)< /COD>,它运行在一个数据集上。内联选择代码后,一切正常:(使用

选项
仅适用于本例,实际代码查看上述给定路径的属性)

let optional=Some(PathBuf::from(“/path/to/dataset/.txt”);
匹配可选。克隆(){
一些(数据集)=>运行(数据集)?,
None=>run(static_dataset())?,
}
但是,我更愿意将(1)“为
数据集
选择一个实现者”的功能从(2)“将
数据集
赋予
运行
功能”中分离出来。我希望使这个功能更可重用(不仅是<代码>运行/代码>函数可以使用(1),并且更独立于<代码>运行< /代码> -考虑对签名的更改,如添加更多的参数。 我尝试了两种方法:

  • 特征对象 虽然我不太乐意仅仅因为解耦的原因而使用trait对象,但我不会从思想上拒绝这个选项。
    fn与_trait_对象(可选:选项)->Box{
    选配{
    一些(数据集)=>Box::new(数据集),
    None=>Box::new(静态_数据集),
    }
    }
    
    但这将被拒绝,并显示错误消息:
      error[E0191]: the value of the associated type `Error` (from trait `Dataset`) must be specified
      --> src/main.rs:68:60
       |
    9  |     type Error: Error + 'static;
       |     ---------------------------- `Error` defined here
    ...
    68 | fn with_trait_object(optional: Option<PathBuf>) -> Box<dyn Dataset> {
       | 
    
  • 我很好奇,因为我无法描述消费者函数的正确签名,所以我似乎无法分解出这段代码。是否有其他方式对类型参数进行排序?或者有完全不同的方法吗


    可以在上找到一个小示例。

    您可以从以下内容开始:

    fn with_consumer_function<DS: Dataset>(
        optional: Option<DS>,
        consumer: impl FnOnce(DS) -> Result<(), Box<dyn Error>>,
    ) -> Result<(), Box<dyn Error>> {
        match optional {
            Some(dataset) => consumer(dataset),
            None => todo!(), //consumer(static_dataset()),
        }
    }
    
    也就是说,使用
    DatasetConsumer
    比使用普通闭包更麻烦


    您也可以尝试将_consumer _function作为宏来实现
    ,但我不确定是否会遵循此路径。

    您可以从以下内容开始:

    fn with_consumer_function<DS: Dataset>(
        optional: Option<DS>,
        consumer: impl FnOnce(DS) -> Result<(), Box<dyn Error>>,
    ) -> Result<(), Box<dyn Error>> {
        match optional {
            Some(dataset) => consumer(dataset),
            None => todo!(), //consumer(static_dataset()),
        }
    }
    
    也就是说,使用
    DatasetConsumer
    比使用普通闭包更麻烦


    您也可以尝试将_consumer _function
    作为宏来实现
    ,但我不确定我是否会遵循这条路径。

    此外,我还曾尝试让编译器接受它。这是你的电话号码


    但我认为@Phimueme有一个非常简洁的回答。在我的例子中,为了方便起见,我添加了一个枚举,而不是一个关联的类型。在代码中,只需将运行闭包映射到
    选项
    ,即可避免泛型。我不确定它是否符合分解匹配臂的标准,但它感觉很方便,并且避免了动态调度。

    此外,我还尝试过让编译器接受它。这是你的电话号码


    但我认为@Phimueme有一个非常简洁的回答。在我的例子中,为了方便起见,我添加了一个枚举,而不是一个关联的类型。在代码中,只需将运行闭包映射到
    选项
    ,即可避免泛型。我不确定它是否符合分解匹配臂的标准,但它感觉很方便,并且避免了动态调度。

    没有trait objects/我不会从思想上拒绝[trait objects]。当你提出一个要求(“没有”)然后说“不要担心这个要求”时,不清楚如何回答你的问题。@Shepmaster,标题中提出这个要求的原因是我一般可以使用trait对象,但在我的具体案例中,trait对象不起作用。如果你对它有不同的说法,我很乐意使用它。没有特质对象/我不会在意识形态上拒绝[特质对象]。当你提出一个要求(“没有”)然后说“不要担心这个要求”时,不清楚如何回答你的问题。@Shepmaster,标题中提出这个要求的原因是我一般可以使用trait对象,但在我的具体案例中,trait对象不起作用。如果您对它有不同的措辞,我很乐意应用它。我希望您指出,使用
    enum
    而不是关联类型可以更灵活地使用此特性。但是,在您的方法中,我看不出如何在另一个使用者函数中进行交换–它们在实际代码中比我在示例中展示的简单的
    使用者
    复杂得多。我希望您指出,使用
    枚举
    而不是关联类型将能够更灵活地使用此特性。但是,在您的方法中,我看不出如何在另一个消费者函数中进行交换–它们在实际代码中比我在示例中展示的简单的
    消费者
    复杂得多