Ответ 1
Ты не можешь. Черты не поддерживают downcasting - Rust не является языком, основанным на наследовании/подтипах, и дает вам другой набор абстракций. Более того, то, что вы хотите сделать, является необоснованным - черты открыты (каждый может реализовать их для чего угодно), так что даже если в вашем случае match *f
охватывает все возможные случаи, в общем, компилятор не может этого знать.
У вас есть два варианта здесь. Если вы знаете множество структур, реализующих вашу черту заранее, просто используйте enum, это идеальный инструмент для этого. Они позволяют статически сопоставлять закрытый набор вариантов:
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 handle different cases:
match f {
FooBar::Foo(x) => println!("it was Foo: {}!", x),
FooBar::Bar(y) => println!("it was Bar: {}!", y),
}
}
Это, безусловно, самый простой способ, и он всегда должен быть предпочтительным.
Другой способ - использовать Any
черту. Это средство для безопасной передачи типов с характерных объектов на обычные типы:
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!() }
}
В идеале должно быть возможно написать что-то вроде этого:
trait Base: Any {}
impl Base for Foo {}
impl Base for Bar {}
и затем использовать Base
в коде, но это невозможно сделать сейчас, потому что наследование признаков не работает с объектами признаков (например, невозможно перейти из Box<Base>
в Base<Any>
).