rust - 用 enum_dispatch 替换 trait 实现者的模式匹配
问题描述
根据这篇文章“不要将装箱的 trait 对象用于 struct internals ”,将 trait 对象存储在 struct 字段中是一个坏主意,因为我们会丢失有关对象的有价值的类型信息。所以他们建议为我们所有的 trait 实现者创建一个枚举包装器。
在下面的示例中,trait Person
有实现者struct Me
和struct Grandma
,我们希望将这两种实现者都存储在 中struct PeopleZoo.people
。
但是因为我们想在PeopleZoo.people
字段中保留关于我们的实现者的类型信息,所以我们创建了一个包装器 enum Person
,而不是使用Box<dyn Person>
:
//our common trait
trait Person {
fn say_hello(&self);
}
//trait implementor 1
struct Me {
name: &'static str,
}
impl Person for Me {
fn say_hello(&self) {
println!("Hello, it's me.")
}
}
struct Grandma {
age: usize
}
impl Person for Grandma {
fn say_hello(&self) {
println!("G'day!")
}
}
enum People {
Grandma(Grandma),
Me(Me)
}
//tedious pattern matching statement
impl Person for People {
fn say_hello(&self) {
match self {
People::Grandma(grandma) => grandma.say_hello(),
People::Me(me) => me.say_hello()
}
}
}
struct PeopleZoo<P: Person> {
people: Vec<P>,
}
impl<P: Person> PeopleZoo<P> {
fn add_person(&mut self, person: P) {
self.people.push(person);
}
fn last_person(&self) -> Option<&P> {
self.people.last()
}
}
fn main() {
let mut zoo: PeopleZoo<People> = PeopleZoo { people: vec![] };
zoo.add_person(People::Me(Me { name: "Bennett" }));
if let Some(People::Me(me)) = zoo.last_person() {
println!("My name is {}.", me.name)
}
}
问题是为我们所有的特征函数编写模式匹配语法Person
会变得相当乏味。据我了解,enum_dispatch将为我们完成一些模式匹配工作,并自动实现Person for People
。但我不确定如何让它工作。
use enum_dispatch::enum_dispatch;
#[enum_dispatch(Person)]
trait Person {
fn say_hello(&self);
}
//implementor struct 1
struct Me {
name: &'static str,
}
impl Person for Me {
fn say_hello(&self) {
println!("Hello, it's me.")
}
}
//implementor struct 2
struct Grandma {
age: usize
}
impl Person for Grandma {
fn say_hello(&self) {
println!("G'day!")
}
}
#[enum_dispatch]
enum People {
Grandma(Grandma),
Me(Me)
}
//I thought enum_dispatch generated this impl for us automatically, so comment out
//impl Person for People {
// fn say_hello(&self) {
// match self {
// People::Grandma(grandma) => grandma.say_hello(),
// People::Me(me) => me.say_hello()
// }
// }
//}
struct PeopleZoo<P: Person> {
people: Vec<P>,
}
impl<P: Person> PeopleZoo<P> {
fn add_person(&mut self, person: P) {
self.people.push(person);
}
fn last_person(&self) -> Option<&P> {
self.people.last()
}
}
fn main() {
let mut zoo: PeopleZoo<People> = PeopleZoo { people: vec![] };
zoo.add_person(People::Me(Me { name: "Bennett" }));
if let Some(People::Me(me)) = zoo.last_person() {
println!("My name is {}.", me.name)
}
}
给出错误:
error[E0599]: the method `last_person` exists for struct `PeopleZoo<People>`, but its trait bounds were not satisfied
--> src\main.rs:63:39
|
30 | enum People {
| ----------- doesn't satisfy `People: Person`
...
44 | struct PeopleZoo<P: Person> {
| --------------------------- method `last_person` not found for this
...
63 | if let Some(People::Me(me)) = zoo.last_person() {
| ^^^^^^^^^^^ method cannot be called on `PeopleZoo<People>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`People: Person`
我是否正确理解 enum_dispatch 的功能?如果是这样,我怎样才能让这个例子工作?
解决方案
改变
#[enum_dispatch(Person)]
trait Person {
fn say_hello(&self);
}
至
#[enum_dispatch(People)]
trait Person {
fn say_hello(&self);
}
并且代码按预期工作。我需要将我们的包装枚举传递enum_dispatch
给我们的特征的属性Person
。
推荐阅读
- robotframework - 忽略 for ...loop Robot 框架中的一项
- google-chrome - 无法通过命令行/puppeteer 启用 google chrome 的标志“Experimental JavaScript”
- python - 从 yaml 中提取不同的值
- html - 如何在表中显示组(上面的 Angular 6)
- reactjs - 如何在reactjs中有条件地隐藏和显示?
- javascript - javascript Proper Tail Recursion 是如何实际实现的?
- java - 指定合作伙伴(来自 CCC 竞赛)
- asp.net-mvc - dropzone 和 asp.net core 3.1 - 发送到控制器的正确参数是什么?
- sql-server - 存储过程更新以删除重复项
- scala - 为什么 Some(1).toString 在scala中不等于“1”?