如何匹配特征实现者 [英] How to match trait implementors
问题描述
我有一个由一些结构实现的特性.我想写一个模式匹配,我可以处理所有可能的情况:
I have a trait that is implemented by some structs. I want to write a pattern match where I can handle every possible case:
trait Base {}
struct Foo {
x: u32,
}
struct Bar {
y: u32,
}
impl Base for Foo {}
impl Base for Bar {}
fn test(v: bool) -> Box<Base + 'static> {
if v {
Box::new(Foo { x: 5 })
} else {
Box::new(Bar { y: 10 })
}
}
fn main() {
let f: Box<Base> = test(true);
match *f {
Foo { x } => println!("it was Foo: {}!", x),
Bar { y } => println!("it was Bar: {}!", y),
}
}
(游乐场)
我收到此编译错误:
error[E0308]: mismatched types
--> src/main.rs:25:9
|
25 | Foo { x } => println!("it was Foo: {}!", x),
| ^^^^^^^^^ expected trait Base, found struct `Foo`
|
= note: expected type `dyn Base`
found type `Foo`
error[E0308]: mismatched types
--> src/main.rs:26:9
|
26 | Bar { y } => println!("it was Bar: {}!", y),
| ^^^^^^^^^ expected trait Base, found struct `Bar`
|
= note: expected type `dyn Base`
found type `Bar`
推荐答案
你不能.Traits 不支持向下转换 - Rust 不是基于继承/子类型的语言,它为您提供了另一组抽象.此外,您想要做的是不健全的 - 特征是开放的(每个人都可以为任何事情实现它们),因此即使在您的情况下 match *f
涵盖所有可能的情况,通常编译器也不能知道这一点.
You can't. Traits do not support downcasting - Rust is not inheritance/subtyping-based language, and it gives you another set of abstractions. Moreover, what you want to do is unsound - traits are open (everyone can implement them for anything), so even if in your case match *f
covers all possible cases, in general the compiler can't know that.
这里有两种选择.如果您事先知道实现您的特征的结构集,只需使用枚举,这是一个完美的工具.它们允许您静态匹配一组封闭的变体:
You have two options here. If you know the set of structures implementing your trait in advance, just use enum, it's a perfect tool for this. They allow you to statically match on a closed set of variants:
enum FooBar {
Foo(u32),
Bar(u32),
}
fn test(v: bool) -> FooBar {
if v {
FooBar::Foo(5)
} else {
FooBar::Bar(10)
}
}
fn main() {
let f: FooBar = test(true);
// Now that we have a `Box<Base>` (`*f` makes it a `Base`),
// let's handle different cases:
match f {
FooBar::Foo(x) => println!("it was Foo: {}!", x),
FooBar::Bar(y) => println!("it was Bar: {}!", y),
}
}
(游乐场)
这是迄今为止最简单的方法,应该始终是首选.
This is by far the simplest way and it should always be preferred.
另一种方法是使用 Any
特性.它是一种从 trait 对象到常规类型的类型安全向下转换的工具:
Another way is to use Any
trait. It is a facility for type-safe downcasting from trait objects to regular types:
use std::any::Any;
struct Foo {
x: u32,
}
struct Bar {
y: u32,
}
fn test(v: bool) -> Box<Any + 'static> {
if v {
Box::new(Foo { x: 5 })
} else {
Box::new(Bar { y: 10 })
}
}
fn main() {
let f: Box<Any> = test(true);
match f.downcast_ref::<Foo>() {
Some(&Foo { x }) => println!("it was Foo: {}!", x),
None => match f.downcast_ref::<Bar>() {
Some(&Bar { y }) => println!("it was Bar: {}!", y),
None => unreachable!(),
},
}
// it will be nicer when `if let` lands
// if let Some(ref Foo { x }) = f.downcast_ref::<Foo>() {
// println!("it was Foo: {}!", x);
// } else if let Some(ref Bar { y }) = f.downcast_ref::<Bar>() {
// println!("it was Bar: {}!", y);
// } else { unreachable!() }
}
(游乐场)
理想情况下应该可以这样写:
Ideally it should be possible to write something like this:
trait Base: Any {}
impl Base for Foo {}
impl Base for Bar {}
然后在代码中使用Base
,但是现在不能做,因为trait继承不适用于trait对象(例如,不可能从Box
到 Base
).
and then use Base
in the code, but it can't be done now because trait inheritance does not work with trait objects (e.g it is impossible to go from Box<Base>
to Base<Any>
).
这篇关于如何匹配特征实现者的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!