Фабричный метод со связанным типом - PullRequest
6 голосов
/ 27 января 2020

Я пытаюсь реализовать фабричный метод, который возвращает 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>;
}

Таким образом, идея состоит в том, что функция sanitize возвращает экземпляр Query, который затем может быть передан функции run.

Завод выглядит так (не compile):

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

Теперь у меня есть только один сервис, и я мог бы указать c в типе Parameters в сигнатуре - что сделает его компиляцией - но я хочу иметь обобщенный c фабричный метод и добавление дополнительных служб.

Вот реализация службы 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> {}
    }
}

Компилятор говорит:

error[E0271]: type mismatch resolving `::Query == Q`
 --> src/main.rs:9:21
  |
9 |         "amazon" => Box::new(amzn::Amazon {}),
  |                     ^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `amzn::Product`
  |
  = note: expected type `Q`
             found type `amzn::Product`
  = help: type parameters must be constrained to match other types
  = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
  = note: required for the cast to the object type `dyn Service`

На основе это сообщение об ошибке, я не уверен, как указать параметр типа. Я попытался извлечь создание Amazon и дать ему явные параметры типа, но это просто оставляет различные ошибки. Кроме того, следование связанной главе 10.02 в книге не дает никаких объяснений по случаю со связанными типами. Наконец, я также попробовал маршрут RF C -1598: Generi c Associated Types , но я не смог ни его скомпилировать, ни уверен, действительно ли он мне нужен.

Также обратите внимание, что я добавил оболочку Box и ограничение QType, основанные на других ответах здесь на SO по аналогичным вопросам, но я мог бы быть совершенно неверным путем здесь.

Любая помощь очень ценится.

1 Ответ

8 голосов
/ 27 января 2020

Эту сигнатуру невозможно реализовать:

fn factory<Q: QType>(name: &str) -> Box<dyn Service<Query = Q>>

Связанный тип всегда однозначно определяется типом реализации. Т.е. каждая реализация Service выбирает только один связанный тип Query.

Это противоречит factory, что позволяет вызывающей стороне решать, каким должен быть связанный тип. Должно быть ясно, что если вы вызываете factory с Q, который не является Product, то код внутри выражения match больше не проверяет тип.

Эту работу можно выполнить, исправив выбор Query:

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

Если вы хотите, чтобы вызывающий абонент выбрал тип, то вам нужно найти способ, чтобы тело функции работало для любого выбора 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")
}

и использовать ее для вашего типа:

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,
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...