Не соответствие ожидаемому типу

Рейтинг: 0Ответов: 1Опубликовано: 16.04.2023
#![feature(type_alias_impl_trait)]
#![feature(return_position_impl_trait_in_trait)]

fn main() {
    let payment = PaymentHelper::get_payment_factory(PaymentType::Sber).create_payment();
    payment.pay(Order::from("November 2022 23:44".to_owned(), 57, "John".to_owned()));
}

struct Order{
    time: String,
    amount: i32,
    name: String,
}

impl Order{
    fn from(time: String, amount: i32, name: String) -> Self {
        Self{time: time, amount: amount, name: name}
    }
    
}

enum PaymentType{
    Sber,
    Vtb
}

trait PaymentFactoryInterface{
    fn create_payment(&self) -> impl PaymentInterface;
}
trait PaymentInterface{
    fn pay(&self, order: Order);
}

struct SberBank;
struct SberBankFactory;

impl PaymentInterface for SberBank {
    fn pay(&self, order: Order) {
        println!("Sber pay success. {} dollars was transfered at {} by {}", order.amount, order.time, order.name);
    }
}

impl PaymentFactoryInterface for SberBankFactory{
    fn create_payment(&self) -> impl PaymentInterface {
        SberBank{}
    }
}

struct VtbBank;
struct VtbBankFactory;

impl PaymentInterface for VtbBank {
    fn pay(&self, order: Order) {
        println!("VTB pay success. {} dollars was transfered at {} by {}", order.amount, order.time, order.name);
    }
}

impl PaymentFactoryInterface for VtbBankFactory{
    fn create_payment(&self) -> impl PaymentInterface {
        VtbBank{}
    }
}


struct PaymentHelper;
impl PaymentHelper {
    fn get_payment_factory(payment_type: PaymentType) -> impl PaymentFactoryInterface {
        match payment_type {
            PaymentType::Vtb => {VtbBankFactory{}}
            PaymentType::Sber => {SberBankFactory{}}
            // _ => {None}
        }
    }
}

Выход:

error[E0308]: `match` arms have incompatible types
  --> src/main.rs:70:35
   |
68 | /         match payment_type {
69 | |             PaymentType::Vtb => {VtbBankFactory{}}
   | |                                  ---------------- this is found to be of type `VtbBankFactory`
70 | |             PaymentType::Sber => {SberBankFactory{}}
   | |                                   ^^^^^^^^^^^^^^^^^ expected `VtbBankFactory`, found `SberBankFactory`
71 | |             // _ => {None}
72 | |         }
   | |_________- `match` arms have incompatible types
   |
help: you could change the return type to be a boxed trait object
   |
67 |     fn get_payment_factory(payment_type: PaymentType) -> Box<dyn PaymentFactoryInterface> {
   |                                                          ~~~~~~~                        +
help: if you change the return type to expect trait objects, box the returned expressions
   |
69 ~             PaymentType::Vtb => {Box::new(VtbBankFactory{})}
70 ~             PaymentType::Sber => {Box::new(SberBankFactory{})}

Как я могу это исправить?

P.S если использовать предложенное исправление то

error[E0038]: the trait `PaymentFactoryInterface` cannot be made into an object
  --> src/main.rs:67:62
   |
67 |     fn get_payment_factory(payment_type: PaymentType) -> Box<dyn PaymentFactoryInterface> {
   |                                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `PaymentFactoryInterface` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> src/main.rs:28:33
   |
27 | trait PaymentFactoryInterface{
   |       ----------------------- this trait cannot be made into an object...
28 |     fn create_payment(&self) -> impl PaymentInterface;
   |                                 ^^^^^^^^^^^^^^^^^^^^^ ...because method `create_payment` references an `impl Trait` type in its return type
   = help: consider moving `create_payment` to another trait

Ответы

▲ 1Принят

У меня работает так:

Тип возвращаемого значения метода get_payment_factory должен быть такой (как и говорит компилятор):

    fn get_payment_factory(payment_type: PaymentType) -> Box<dyn PaymentFactoryInterface> {
    //                                                   ^^^^^^^                        ^

Но только этого недостаточно. Также нужно изменить тип в методе PaymentFactoryInterface.create_payment:

    fn create_payment(&self) -> Box<dyn PaymentInterface>;
    //                          ^^^^^^^                 ^

И, соответственно в реализациях наследников:

impl PaymentFactoryInterface for SberBankFactory{
    fn create_payment(&self) -> Box<dyn PaymentInterface> {
    //                          ^^^^^^^                 ^
        SberBank{}
    }
}

и

impl PaymentFactoryInterface for VtbBankFactory{
    fn create_payment(&self) -> Box<dyn PaymentInterface> {
    //                          ^^^^^^^                 ^
        VtbBank{}
    }
}

Затем необходимо изменить способ создания экземпляра в этих методах:

impl PaymentFactoryInterface for SberBankFactory{
    fn create_payment(&self) -> Box<dyn PaymentInterface> {
        Box::new(SberBank{})
    //  ^^^^^^^^^          ^
    }
}

и

impl PaymentFactoryInterface for VtbBankFactory{
    fn create_payment(&self) -> Box<dyn PaymentInterface> {
        Box::new(VtbBank{})
    //  ^^^^^^^^^         ^
    }
}

Ну, и наконец, изменить способ создания экземпляра в match:

        match payment_type {
            PaymentType::Vtb => {Box::new(VtbBankFactory{})}
            //                   ^^^^^^^^^                ^
            PaymentType::Sber => {Box::new(SberBankFactory{})}
            //                    ^^^^^^^^^                 ^
            // _ => {None}
        }

Кстати, теперь #![future(...)] в начале не нужны.