有没有一种方法可以对具有实现特征的字段的枚举变体调用特征方法



我的枚举有40种变体,其中大约一半实现了特性,但这里有一个更简单的例子:

trait CheeseBoard {
fn say_cheese(self);
}
struct Cheese {
name: String,
}
impl CheeseBoard for Cheese {
fn say_cheese(self) {
println!("I am {}", self.name);
}
}
struct Person {
name: String,
}
impl CheeseBoard for Person {
fn say_cheese(self) {
println!("{} says cheese!", self.name);
}
}
enum CheesyPerson {
Cheese(Cheese),
Person(Person),
UncheesyNonperson,
}
fn main() {
let _a = [
CheesyPerson::Cheese(Cheese {
name: "Gouda".into(),
}),
CheesyPerson::Person(Person {
name: "Peer".into(),
}),
CheesyPerson::UncheesyNonperson,
];
todo!("Call say_cheese on items in _a where the enum variant has exactly one field that implements the CheeseBoard trait.")
}

这在Rust中是不可能的;它甚至没有机制来匹配";具有单个值的任何枚举可能性;更不用说实现某个特定特性了。

我能想到的实现这一点的最干净的方法是使用一个返回Option<&dyn CheeseBoard>:的helper方法

impl CheesyPerson {
fn get_cheese_board(&self) -> Option<&dyn CheeseBoard> {
match self {
Self::Cheese(v) => Some(v),
Self::Person(v) => Some(v),
_ => None,
}
}
}

现在你可以做这样的事情:

for v in _a.iter().filter_map(|v| v.get_cheese_board()) {
v.say_cheese();
}

请注意,这需要更改CheeseBoard::say_cheese方法,因为现在它按值获取self,在这个过程中消耗CheeseBoard。该方法需要参考self

trait CheeseBoard {
fn say_cheese(&self);
//            ^
// Add this to take self by reference
}

(游乐场(

谢谢@cdhowie,你的评论让我免于又一个小时的折磨!

如果有人用另一层结构包裹枚举,请确保借用枚举,而不是复制它。例如,

struct RoomForOne(CheesyPerson);
impl RoomForOne {
fn get_cheese_board(&self) -> Option<&dyn CheeseBoard> {
match &self.0 { // & here is important!
CheesyPerson::Cheese(v) => Some(v),
CheesyPerson::Person(v) => Some(v),
_ => None,
}
}
}

最新更新