我想做一个程序,使用命令行参数来选择使用的结构。像这样的东西:
trait Animal {
fn sound(&self) -> &str;
}
struct Dog {}
impl Animal for Dog {
fn sound(&self) -> &str {
"Woof!"
}
}
struct Cat {}
impl Animal for Cat {
fn sound(&self) -> &str {
"Miau!"
}
}
trait Place {
fn name(&self) -> &str;
}
struct Museum {}
impl Place for Museum {
fn name(&self) -> &str {
"museum"
}
}
struct Hotel{}
impl Place for Hotel {
fn name(&self) -> &str {
"hotel"
}
}
fn get_animal(animal: &str) -> Box<dyn Animal> {
match animal {
"dog" => Box::new(Dog{}),
"cat" => Box::new(Cat{}),
_ => todo!()
}
}
fn get_place(place: &str) -> Box<dyn Place> {
match place {
"museum" => Box::new(Museum{}),
"hotel" => Box::new(Hotel{}),
_ => todo!()
}
}
fn sentence(animal: Box<dyn Animal>, place: Box<dyn Place>) {
println!("{} We are at {}.", animal.sound(), place.name());
}
fn main() {
let args: Vec<String> = env::args().collect();
let animal = get_animal(&args[1]);
let place = get_place(&args[2]);
sentence(animal, place);
}
但是,我使用的特征不是对象安全的,所以我不能使用
dyn
。现在我只是对每个组合使用 match
:
match (animal, place) {
("dog", "museum") => sentence(Dog{}, Museum{}),
("dog", "hotel") => sentence(Dog{}, Hotel{}),
("cat", "museum") => sentence(Cat{}, Museum{}),
("cat", "hotel") => sentence(Cat{}, Hotel{}),
}
但是,我将向我的程序添加更多结构,因此这是不可扩展的。有没有可扩展的方法来解决这个问题?是否可以创建一个生成
match
的宏,这样我就不必手动编写它,或者是否有更好的解决方案?
我正在使用的特征是:
修改特征以使其对象安全可能不是一个好主意,原因有两个:
特质不是对象安全的,基本上有两个原因:
self
参数)。如果您需要调用这样的特征方法,那么您将需要使用
match
,就像您正在做的 (*) 一样。通过使用板条箱,例如箱子,可以使这更符合人体工程学。 enum_dispatch
.
如果您需要调用的方法都是对象安全的,则可以使用仅包含对象安全方法的总体实现来创建包装器特征:
use std::fmt::Debug;
trait NotObjectSafe {
fn generic_method<T: Debug> (&self, val: &T);
fn static_method();
fn object_safe_method (&self);
}
impl NotObjectSafe for i32 {
fn generic_method<T: Debug> (&self, val: &T) {
println!("Generic method for {self}: {val:?}");
}
fn static_method() {
println!("Static method for i32");
}
fn object_safe_method (&self) {
println!("Object safe method for {self}");
}
}
trait ObjectSafeWrapper {
fn object_safe_method (&self);
}
impl<T: NotObjectSafe> ObjectSafeWrapper for T {
fn object_safe_method (&self) {
NotObjectSafe::object_safe_method (self)
}
}
fn main() {
let i = 42;
let r: &dyn ObjectSafeWrapper = &i;
r.object_safe_method();
}
(*) 如果您需要访问有限数量的泛型方法,您仍然可以创建包装器特征,但您需要为您计划使用的类型提供泛型方法的非泛型版本:
use std::fmt::Debug;
trait NotObjectSafe {
fn generic_method<T: Debug> (&self, val: &T);
}
impl NotObjectSafe for i32 {
fn generic_method<T: Debug> (&self, val: &T) {
println!("Generic method for {self}: {val:?}");
}
}
trait ObjectSafeWrapper {
fn generic_method_on_f32 (&self, val: &f32);
}
impl<T: NotObjectSafe> ObjectSafeWrapper for T {
fn generic_method_on_f32 (&self, val: &f32) {
NotObjectSafe::generic_method (self, val)
}
}
fn main() {
let i = 42;
let r: &dyn ObjectSafeWrapper = &i;
r.generic_method_on_f32 (&3.14);
}