Rust 错误“您可以将找到的值装箱并将其强制为特征对象”是什么意思?

问题描述 投票:0回答:1

这个错误是什么意思?

复制

error[E0308]: mismatched types
  --> src/main.rs:50:35
   |
50 |           PaymentType::InvoiceIn => InvoiceIn {
   |  ___________________________________^
51 | |             name: "Invoice 1".to_string(),
52 | |             payed: false,
53 | |         },
   | |_________^ expected `dyn Payable`, found `InvoiceIn`
   |
   = note: expected trait object `dyn Payable`
                    found struct `InvoiceIn`
   = help: `InvoiceIn` implements `Payable` so you could box the found value and coerce it to the trait object `Box<dyn Payable>`, you will have to change the expected type as well

For more information about this error, try `rustc --explain E0308`.

代码:

trait Payable {
    fn toggle_payed(&mut self);
}

enum PaymentType {
    InvoiceOut,
    InvoiceIn,
}

struct Payment {
    r#type: PaymentType,
}

struct InvoiceOut {
    name: String,
    payed: bool,
}

impl Payable for InvoiceOut {
    fn toggle_payed(&mut self) {
        self.payed = !self.payed
    }
}

struct InvoiceIn {
    name: String,
    payed: bool,
}

impl Payable for InvoiceIn {
    fn toggle_payed(&mut self) {
        self.payed = !self.payed
    }
}

fn save_invoice_in(invoice: &InvoiceIn) {
    println!("{}", invoice.name)
}

fn save_invoice_out(invoice: &InvoiceOut) {
    println!("{}", invoice.name)
}

fn main() {
    let payment = Payment {
        r#type: PaymentType::InvoiceOut, // This comes from user!
    };

    let payable: dyn Payable = match payment.r#type {
        PaymentType::InvoiceIn => InvoiceIn {
            name: "Invoice 1".to_string(),
            payed: false,
        },
        PaymentType::InvoiceOut => InvoiceOut {
            name: "Invoice 2".to_string(),
            payed: false,
        },
    };

    // Do something else with payable here

    payable.toggle_payed();

    // Do something else with payable here

    match payment.r#type {
        PaymentType::InvoiceIn => save_invoice_in(&payable),
        PaymentType::InvoiceOut => save_invoice_out(&payable),
    };
}
rust coercion
1个回答
0
投票

这里尝试展示评论中提到的两种可能性。

这个例子分为三个部分:

  • “通用”部分仅包含问题的基本元素,没有任何解决方案来获得您期望的调度和向下转型,
  • “动态”部分使用您试图实现的动态调度;它主要在于使用
    std::any::Any
    来向下转换为原始类型,
  • “enum”部分以更传统的方式执行相同的操作,依赖于
    enum

理论上,“动态”解决方案可以比“公共”部分中存在的变体开放更多的变体(在其他地方实现),但由于您似乎需要某种向下转型,所以我认为不需要真的很适合你的需要。

“枚举”解决方案对我来说看起来最好,特别是因为您似乎需要向下转型为有限数量的变体(在枚举中一次性知道)。 请注意,

enum_dispatch
箱可以帮助通过变体转发方法调用。

mod common_part {
    pub enum PaymentType {
        InvoiceOut,
        InvoiceIn,
    }
    pub struct Payment {
        pub r#type: PaymentType,
    }

    pub struct InvoiceIn {
        pub name: String,
        pub payed: bool,
    }

    pub struct InvoiceOut {
        pub name: String,
        pub payed: bool,
    }

    pub trait Payable {
        fn toggle_payed(&mut self);
    }

    impl Payable for InvoiceIn {
        fn toggle_payed(&mut self) {
            self.payed = !self.payed
        }
    }

    impl Payable for InvoiceOut {
        fn toggle_payed(&mut self) {
            self.payed = !self.payed
        }
    }

    pub fn save_invoice_in(invoice: &InvoiceIn) {
        println!("save_invoice_in: {}", invoice.name)
    }

    pub fn save_invoice_out(invoice: &InvoiceOut) {
        println!("save_invoice_out: {}", invoice.name)
    }
}

mod dyn_part {
    use super::common_part::*;

    pub trait DynPayable: Payable {
        fn as_any(&self) -> &dyn std::any::Any;
    }

    impl DynPayable for InvoiceIn {
        fn as_any(&self) -> &dyn std::any::Any {
            self
        }
    }

    impl DynPayable for InvoiceOut {
        fn as_any(&self) -> &dyn std::any::Any {
            self
        }
    }
}

mod enum_part {
    use super::common_part::*;

    pub enum Invoice {
        In(InvoiceIn),
        Out(InvoiceOut),
    }

    impl Payable for Invoice {
        fn toggle_payed(&mut self) {
            match self {
                Self::In(invoice) => invoice.toggle_payed(),
                Self::Out(invoice) => invoice.toggle_payed(),
            }
        }
    }
}

fn main() {
    use common_part::*;
    let payments = [
        // This comes from user!
        Payment {
            r#type: PaymentType::InvoiceIn,
        },
        Payment {
            r#type: PaymentType::InvoiceOut,
        },
    ];
    {
        use dyn_part::*;
        println!("~~~~ using dynamic dispatch ~~~~");
        for payment in payments.iter() {
            let mut payable: Box<dyn DynPayable> = match payment.r#type {
                PaymentType::InvoiceIn => Box::new(InvoiceIn {
                    name: "Invoice 1".to_string(),
                    payed: false,
                }),
                PaymentType::InvoiceOut => Box::new(InvoiceOut {
                    name: "Invoice 2".to_string(),
                    payed: false,
                }),
            };
            payable.toggle_payed();
            if let Some(invoice) =
                payable.as_any().downcast_ref::<InvoiceIn>()
            {
                save_invoice_in(invoice);
            }
            if let Some(invoice) =
                payable.as_any().downcast_ref::<InvoiceOut>()
            {
                save_invoice_out(invoice);
            }
        }
    }
    {
        use enum_part::*;
        println!("~~~~ using an enum ~~~~");
        for payment in payments.iter() {
            let mut payable = match payment.r#type {
                PaymentType::InvoiceIn => Invoice::In(InvoiceIn {
                    name: "Invoice 1".to_string(),
                    payed: false,
                }),
                PaymentType::InvoiceOut => Invoice::Out(InvoiceOut {
                    name: "Invoice 2".to_string(),
                    payed: false,
                }),
            };
            payable.toggle_payed();
            match &mut payable {
                Invoice::In(invoice) => save_invoice_in(invoice),
                Invoice::Out(invoice) => save_invoice_out(invoice),
            }
        }
    }
}
/*
~~~~ using dynamic dispatch ~~~~
save_invoice_in: Invoice 1
save_invoice_out: Invoice 2
~~~~ using an enum ~~~~
save_invoice_in: Invoice 1
save_invoice_out: Invoice 2
*/
© www.soinside.com 2019 - 2024. All rights reserved.