我的枚举有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,
}
}
}