这个错误是什么意思?
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),
};
}
这里尝试展示评论中提到的两种可能性。
这个例子分为三个部分:
std::any::Any
来向下转换为原始类型,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
*/