如何在 Rust 中将变量设置为泛型类型 Trait 的实现?

发布于 2025-01-17 02:20:32 字数 2848 浏览 2 评论 0原文

我是 Rust 新手,我正在尝试编写一个应用程序,该应用程序基本上使用许多可能的服务之一来获取一些数据,转换它并保存到我的数据库中。

我正在尝试做一些类似 Java 通用接口的事情,根据命令行输入实例化正确的服务并在整个程序中使用它。

我编写了类似这样的代码:

use anyhow::Result;

pub trait Service<T> {
    fn get_values(&self, id: u64) -> T;
    fn convert_data(&self, input: T, output: &mut Vec<MyDataStruct>) -> Result<String>;
}

以及两种不同的实现:

struct ServiceA {
    clientA: DataRetrieverForA,
}

struct ServiceB {
    clientB: DataRetrieverForB,
}

impl Service<Result<ADataStruct>> for ServiceA {
    fn get_values(&self, id: u64) -> Result<ADataStruct>;
    fn convert_data(&self, input: Result<ADataStruct>, output: &mut Vec<MyDataStruct>) -> Result<String>;
}

impl Service<BDataStruct> for ServiceB {
    fn get_values(&self, id: u64) -> BDataStruct;
    fn convert_data(&self, input: BDataStruct, output: &mut Vec<MyDataStruct>) -> Result<String>;
}

self.get_values(id)使用self.clientX检索数据和self.convert_data(input , &mut output) 将数据从 ADataStructBDataStruct 转换为 MyDataStruct,然后再将其保存到我的数据库中。

该应用程序将使用 ServiceAServiceB 运行,具体取决于命令行输入:

fn main() {
    // ...

    let service: Box<&(dyn Service<_>)> = match cli_service {
        Service::A => { Box::new(&ServiceA::new(...) }
        Service::B => { Box::new(&ServiceB::new(...) }
    };

    //...
}

我尝试了很多更改,主要基于 https://doc.rust-lang.org/book/ch10-02-traits.htmlhttps://doc.rust-lang.org/book/ch17- 02-trait-objects.html 但我找不到处理使用特征定义中的泛型类型的函数的示例。当我删除通用 参数并将其固定为一些通用结构以进行测试时,代码编译并运行时没有错误。所以我的猜测是我的错误在于通用/特征的使用。

我用这段代码得到的错误:


error[E0277]: the trait bound `ServiceB: Service<ADataStruct>` is not satisfied
  --> ori-runner\src\main.rs:40:37
   |
40 | ... { Box::new(&ServiceB::new(params)...
   |       -------- ^^^^^^^^^^^^^^^^^^^^^^ the trait `Service<ADataStruct>` is not implemented for `ServiceB`
   |       |
   |       required by a bound introduced by this call
   |
   = help: the following implementations were found:
             <ServiceB as Service<BDataStructure>>
   = note: required for the cast to the object type `dyn Service<ADataStruct>>`

我做错了什么?很明显,第一个匹配类型是定义 dyn Service 变量的“_”,但我没有想法和谷歌搜索......

谢谢!

I am new to rust and I am trying to write an app that basically uses one of many possible services to fetch some data, transform it and save to my database.

I am trying to do something like a generic interface from Java, to instantiate the correct service based on command line input and use it throughout the program.

I have coded something like this:

use anyhow::Result;

pub trait Service<T> {
    fn get_values(&self, id: u64) -> T;
    fn convert_data(&self, input: T, output: &mut Vec<MyDataStruct>) -> Result<String>;
}

and two different implementations:

struct ServiceA {
    clientA: DataRetrieverForA,
}

struct ServiceB {
    clientB: DataRetrieverForB,
}

impl Service<Result<ADataStruct>> for ServiceA {
    fn get_values(&self, id: u64) -> Result<ADataStruct>;
    fn convert_data(&self, input: Result<ADataStruct>, output: &mut Vec<MyDataStruct>) -> Result<String>;
}

impl Service<BDataStruct> for ServiceB {
    fn get_values(&self, id: u64) -> BDataStruct;
    fn convert_data(&self, input: BDataStruct, output: &mut Vec<MyDataStruct>) -> Result<String>;
}

the self.get_values(id) uses the self.clientX to retrieve data and self.convert_data(input, &mut output) transforms data from ADataStruct and BDataStruct to MyDataStruct before saving it to my database.

The app will run using either ServiceA or ServiceB depending on command line input:

fn main() {
    // ...

    let service: Box<&(dyn Service<_>)> = match cli_service {
        Service::A => { Box::new(&ServiceA::new(...) }
        Service::B => { Box::new(&ServiceB::new(...) }
    };

    //...
}

I have tried many changes, mostly based on https://doc.rust-lang.org/book/ch10-02-traits.html and https://doc.rust-lang.org/book/ch17-02-trait-objects.html but I can't find an example that handles functions that use a generic type from trait definition. When I removed the generic parameter and fixed it to some common struct for testing, the code compiled and ran with no errors. So my guess is my mistake is with generic/trait usage.

The error I get with this code:


error[E0277]: the trait bound `ServiceB: Service<ADataStruct>` is not satisfied
  --> ori-runner\src\main.rs:40:37
   |
40 | ... { Box::new(&ServiceB::new(params)...
   |       -------- ^^^^^^^^^^^^^^^^^^^^^^ the trait `Service<ADataStruct>` is not implemented for `ServiceB`
   |       |
   |       required by a bound introduced by this call
   |
   = help: the following implementations were found:
             <ServiceB as Service<BDataStructure>>
   = note: required for the cast to the object type `dyn Service<ADataStruct>>`

What am I doing wrong? It is obvious the first match type is defining the '_' of the dyn Service variable, but I am out of ideas and google searches...

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

影子的影子 2025-01-24 02:20:32

由于类型不同,一种选择是将它们包装在枚举中,并使用一些方法来根据决策计算所需的任何内容。枚举包装器将抽象服务操作。

struct DataRetrieverForA {}
struct DataRetrieverForB {}

struct ADataStruct {}
struct BDataStruct {}
struct MyDataStruct {}

struct ServiceA {
    clientA: DataRetrieverForA,
}

struct ServiceB {
    clientB: DataRetrieverForB,
}

impl ServiceA {
    fn get_values(&self, id: u64) -> Result<ADataStruct, ()> {
        Ok(ADataStruct {})
    }
    fn convert_data(
        &self,
        input: Result<ADataStruct, ()>,
        output: &mut Vec<MyDataStruct>,
    ) -> Result<String, ()> {
        Ok("foo".into())
    }
}

impl ServiceB {
    fn get_values(&self, id: u64) -> BDataStruct {
        BDataStruct {}
    }
    fn convert_data(
        &self,
        input: BDataStruct,
        output: &mut Vec<MyDataStruct>,
    ) -> Result<String, ()> {
        Ok("bar".into())
    }
}

enum Services {
    A(ServiceA),
    B(ServiceB),
}

impl Services {
    fn a() -> Self {
        Self::A(ServiceA {
            clientA: DataRetrieverForA {},
        })
    }

    fn b() -> Self {
        Self::B(ServiceB {
            clientB: DataRetrieverForB {},
        })
    }

    fn compute(self) {
        todo!()
    }
}

fn main() {
    let arg = "a";

    let service = match arg {
        "a" => Services::a(),
        _ => Services::b(),
    };
    service.compute();
}

游乐场

Since the types are different, one option would be to wrap them in an enum and have some method/s for computing whatever needed depending on the decision. The enum wrapper would abstract the services operations.

struct DataRetrieverForA {}
struct DataRetrieverForB {}

struct ADataStruct {}
struct BDataStruct {}
struct MyDataStruct {}

struct ServiceA {
    clientA: DataRetrieverForA,
}

struct ServiceB {
    clientB: DataRetrieverForB,
}

impl ServiceA {
    fn get_values(&self, id: u64) -> Result<ADataStruct, ()> {
        Ok(ADataStruct {})
    }
    fn convert_data(
        &self,
        input: Result<ADataStruct, ()>,
        output: &mut Vec<MyDataStruct>,
    ) -> Result<String, ()> {
        Ok("foo".into())
    }
}

impl ServiceB {
    fn get_values(&self, id: u64) -> BDataStruct {
        BDataStruct {}
    }
    fn convert_data(
        &self,
        input: BDataStruct,
        output: &mut Vec<MyDataStruct>,
    ) -> Result<String, ()> {
        Ok("bar".into())
    }
}

enum Services {
    A(ServiceA),
    B(ServiceB),
}

impl Services {
    fn a() -> Self {
        Self::A(ServiceA {
            clientA: DataRetrieverForA {},
        })
    }

    fn b() -> Self {
        Self::B(ServiceB {
            clientB: DataRetrieverForB {},
        })
    }

    fn compute(self) {
        todo!()
    }
}

fn main() {
    let arg = "a";

    let service = match arg {
        "a" => Services::a(),
        _ => Services::b(),
    };
    service.compute();
}

Playground

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文