gpt4 book ai didi

generics - 如何在Rust中没有特征对象的特征的多个实现者之间进行选择?

转载 作者:行者123 更新时间:2023-12-03 11:42:56 25 4
gpt4 key购买 nike

我有一个称为Dataset的特征,它具有一个通过文件名和关联的Error类型检索文件内容的方法。该特征是针对不同的文件系统结构实现的,例如zipt_code(用于基于目录的访问)或PathBuf(用于在压缩的zip归档文件中进行访问)。为了测试目的,还存在ZipArchive的实现。

trait Dataset {
type Error: Error + 'static;

fn read(&mut self, name: &str) -> Result<String, Self::Error>;
}
现在,我想根据给定的文件名推断要使用的实现方式:目录,如果路径是目录,则是zip归档文件,如果扩展名是 HashMap.zip,等等。请考虑对数据集进行操作的 .bzip函数。内联选择代码,一切正常:(仅 fn run(dataset: impl Dataset)仅用于此示例,实际代码如上所述查看给定路径的属性)
let optional = Some(PathBuf::from("/path/to/dataset/.txt"));
match optional.clone() {
Some(dataset) => run(dataset)?,
None => run(static_dataset())?,
}
但是,我希望将(1)“选择 Option<PathBuf>的实现者”与(2)“将 Dataset赋予 Dataset函数”的功能分开。我希望使此函数更具可重用性(因为不仅 run函数可以使用(1)),而且可以更独立于 run的更改–考虑对签名进行更改,例如添加更多参数。
我尝试了两种方法:
  • 特性对象
    尽管我不很乐意仅出于脱钩的原因使用特征对象,但我不会在意识形态上拒绝此选项。
    fn with_trait_object(optional: Option<PathBuf>) -> Box<dyn Dataset> {
    match optional {
    Some(dataset) => Box::new(dataset),
    None => Box::new(static_dataset),
    }
    }
    但这被错误消息拒绝:
      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> {
    |
    这对我来说很有意义,因为run类型位于返回位置,并且在使用trait对象时我们不知道它的确切大小。
  • 闭包作为参数
    我的第二种方法是关于它的想法,我更喜欢:与其将Dataset::Error返回给我的调用者,调用者为我提供了一个接受Dataset的闭包。然后,可以使用闭包将其他参数传递给实际的处理函数。我还可以使用不同的闭包,以便可以轻松地重用“选择正确的实现器”功能。
    但是,我无法表示“此函数采用闭包Dataset,它本身在第一个参数中的FnOnce特性上通用”。此刻,我使用Dataset作为闭包的返回类型,但是在那里也泛型也很好。这里是错误的代码:
    fn with_consumer_function(
    optional: Option<PathBuf>,
    consumer: impl FnOnce(impl Dataset) -> Result<(), Box<dyn Error>>,
    ) -> Result<(), Box<dyn Error>> {
    match optional {
    Some(dataset) => consumer(dataset),
    None => consumer(static_dataset),
    }
    }

  • 我很好奇,我无法描述这段代码,因为我无法描述使用者函数的正确签名。是否有其他方式订购类型参数?还是有可能完全不同的方法?
    可以在 Rust Playground上找到一个小例子。

    最佳答案

    您可以从以下开始:

    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()),
    }
    }
    如果要更改 None臂,则必须创建(通用)方法来生成数据集。您可以通过使用 Dataset扩展特征 fn static_dataset() -> Self来实现。 None无法与闭包一起使用的原因是rust(至少到目前为止)将rust视为接受固定参数的闭包。如果 consumer在第一个分支中接受 DS,则不能简单地在第二个分支中接受 HashMap-它仍必须接受相同的类型: DS
    您可以通过定义自己的特征来解决此问题:
    trait DatasetConsumer {
    fn call<DS: Dataset>(self, ds: DS) -> Result<(), Box<dyn Error>>;
    }

    struct DatasetConsumerRun;
    impl DatasetConsumer for DatasetConsumerRun {
    fn call<DS: Dataset>(self, mut dataset: DS) -> Result<(), Box<dyn Error>> {
    println!("{}", dataset.read("info.txt")?);
    Ok(())
    }
    }

    fn with_consumer_function<DS: Dataset>(
    optional: Option<DS>,
    consumer: impl DatasetConsumer,
    ) -> Result<(), Box<dyn Error>> {
    match optional {
    Some(dataset) => consumer.call(dataset),
    None => consumer.call(static_dataset()),
    }
    }

    fn main() -> Result<(), Box<dyn Error>> {
    let optional = Some(PathBuf::from("/path/to/dataset/.txt"));
    with_consumer_function(optional.clone(), DatasetConsumerRun)?;
    Ok(())
    }
    就是说,使用 DatasetConsumer比使用纯闭包更麻烦。
    您也可以尝试将 with_consumer_function实现为宏,但是我不确定是否会遵循此路径。

    关于generics - 如何在Rust中没有特征对象的特征的多个实现者之间进行选择?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63229204/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com