Что означает эта ошибка?
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),
};
}
Вот на самом деле минимальный воспроизводимый пример пожалуйста, всегда старайтесь максимально сократить пример перед публикацией.
@ cafce25, если я не понимаю, что происходит, я не могу понять, как это уменьшить. РЖУ НЕ МОГУ
Если я использую Box<dyn Payable>, я не знаю, как получить от него счет-фактуру: play.rust-lang.org/…
Вы не можете, но и для dyn Payable это не будет ничем отличаться: вам нужно добавить к признаку всю необходимую функциональность.
О боже мой. Можете ли вы объяснить мне, как решить эту проблему?
Используйте Box<dyn Payable> именно то, что говорит вам компилятор.
Да, но мне нужен счет от кредитора.
Затем вам нужно добавить в признак всю необходимую функциональность. Например, доступ к нужным вам полям.
Что ты имеешь в виду? У меня есть много методов, которым нужен счет-фактура. Должен ли я вместо этого создать другие методы с аргументом «оплата»? Это безумие!
Просто укажите struct Invoice { direction: Direction, … }, где Direction — это enum Direction { Out, In }, а затем добавьте к этому свои методы struct.
Вероятно, вам следует переформулировать свой вопрос, потому что на вопрос, который вы задали в посте, есть ответ, в котором вам предлагается использовать Box<dyn Payable>, но судя по вашим комментариям, ваш реальный вопрос выглядит другим.
Вместо черты вы можете создать enum Invoice { In(InvoiceIn), Out(InvoiceOut) }. Общие для обоих методы (которые теперь есть в признаке) будут реализованы в перечислении.
«Если я использую Box<dyn Payable>, я не знаю, как получить из него Invoice» → Как получить ссылку на конкретный тип из объекта типажа?

Вот попытка показать две возможности, упомянутые в комментариях.
Этот пример разделен на три части:
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
*/
dyn Traitточно так же, какstrедва ли может существовать в стеке, потому что оба не имеют размера (не имеют статически известного размера), вам нужна некоторая косвенность, напримерBoxили ссылка.