具有关联类型的工厂方法

ret*_*hab 6 rust

我正在尝试实现一个返回Service具有关联类型的工厂方法。我让它在没有关联类型的情况下工作,但是一旦我添加了它,无论我如何按摩它,我都无法编译它..

这是Service

trait QType {}

trait Service {
    type Query: QType;

    fn sanitize(&self, query: &str) -> Result<Self::Query, String>;

    fn run(&self, query: &Self::Query) -> Result<(), String>;
}
Run Code Online (Sandbox Code Playgroud)

所以想法是sanitize函数返回 的一个实例Query,然后可以将其传递给run函数。

工厂看起来像这样(不编译):

fn factory<Q: QType>(name: &str) -> Box<dyn Service<Query = Q>> {
    match name {
        "amazon" => Box::new(amzn::Amazon {}),
        other => panic!("Invalid service {}", other),
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我这里只有一个服务,我可以在签名中的类型参数中进行特定——这将使它编译——但我​​想要一个通用的工厂方法并添加更多服务。

下面是Amazon服务的实现:

mod amzn {
    use super::*;

    pub struct Amazon {}

    pub struct Product {
        name: String,
    }

    impl QType for Product {}

    impl Service for Amazon {
        type Query = Product;
        fn sanitize(&self, query: &str) -> Result<Product, String> {}
        fn run(&self, query: &Product) -> Result<(), String> {}
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器说:

错误[E0271]:类型不匹配解决`::Query == Q`
 --> src/main.rs:9:21
  |
9 | “亚马逊” => Box::new(amzn::Amazon {}),
  | ^^^^^^^^^^^^^^^^^^^^^^^^^ 预期类型参数,找到结构`amzn::Product`
  |
  = 注意:预期类型`Q`
             找到类型`amzn::Product`
  = help: 必须约束类型参数以匹配其他类型
  = 注意:有关更多信息,请访问 https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
  = 注意:需要转换为对象类型 `dyn Service`

基于此错误消息,我不确定如何指定类型参数。我曾尝试提取创建Amazon并为其提供显式类型参数,但这只会导致不同的错误。此外,遵循书中链接的第 10.02 章没有对关联类型的情况进行任何解释。最后,我还尝试了RFC-1598: Generic Associated Types的路线,但我既无法编译它,也不确定我是否真的需要它。

另请注意,我在 SO 上围绕类似问题添加了基于其他答案的Box包装器和QType限制,但我在这里可能完全走错了路。

任何帮助深表感谢。

Pet*_*all 8

此签名无法实现:

fn factory<Q: QType>(name: &str) -> Box<dyn Service<Query = Q>>
Run Code Online (Sandbox Code Playgroud)

关联类型始终由实现类型唯一确定。即每个实现Service只选择一个关联类型Query

这与 不一致factory,后者让调用者决定关联的类型应该是什么。应该很清楚地看到,如果您factory使用Qnot调用,则表达式中Product的代码match不再进行类型检查。

您可以通过修复以下选项来完成这项工作Query

fn factory(name: &str) -> Box<dyn Service<Query = Product>> {
    match name {
        "amazon" => Box::new(amzn::Amazon {}),
        other => panic!("Invalid service {}", other),
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您希望调用者选择类型,那么您需要找到一种方法,以便函数体可以为任何选择的Q. 例如,您可以将构造与QType特征相关联:

trait QType {
    fn create_service(name: &str) -> Option<Box<dyn Service<Query = Self>>>;
}

fn factory<Q: QType>(name: &str) -> Box<dyn Service<Query = Q>> {
    Q::create_service(name).expect("Invalid service")
}
Run Code Online (Sandbox Code Playgroud)

并为您的类型实施:

impl QType for Product {
    fn create_service(name: &str) -> Option<Box<dyn Service<Query = Self>>> {
        match name {
            "amazon" => Some(Box::new(amzn::Amazon {})),
            other => None,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)