Rust:对向量中结构的多态调用
Rust: polymorphic calls for structs in a vector
我是 Rust 的新手,我正在尝试了解该语言的基础知识。
考虑以下特征
trait Function {
fn value(&self, arg: &[f64]) -> f64;
}
和两个实现它的结构:
struct Add {}
struct Multiply {}
impl Function for Add {
fn value(&self, arg: &[f64]) -> f64 {
arg[0] + arg[1]
}
}
impl Function for Multiply {
fn value(&self, arg: &[f64]) -> f64 {
arg[0] * arg[1]
}
}
在我的 main()
函数中,我想将 Add
和 Multiply
的两个实例分组到一个向量中,然后调用 value
方法。以下作品:
fn main() {
let x = vec![1.0, 2.0];
let funcs: Vec<&dyn Function> = vec![&Add {}, &Multiply {}];
for f in funcs {
println!("{}", f.value(&x));
}
}
还有:
fn main() {
let x = vec![1.0, 2.0];
let funcs: Vec<Box<dyn Function>> = vec![Box::new(Add {}), Box::new(Multiply {})];
for f in funcs {
println!("{}", f.value(&x));
}
}
有没有更好/更简洁的方法?我可以解决将实例包装在 Box
中的问题吗?在这种情况下,特征对象的要点是什么?
Is there any better / less verbose way?
确实没有办法使它不那么冗长。由于您使用的是特征对象,因此您需要告诉编译器向量的项是 dyn Function
而不是具体类型。编译器不能仅仅推断出您指的是 dyn Function
特征对象,因为 Add
和 Multiply
都可以实现其他特征。
您也无法抽象出对 Box::new
的调用。为此,您必须以某种方式映射异构集合,这在 Rust 中是不可能的。但是,如果你经常写这个,你可以考虑为每个具体的 impl
:
添加辅助构造函数
impl Add {
fn new() -> Add {
Add {}
}
fn new_boxed() -> Box<Add> {
Box::new(Add::new())
}
}
尽可能包含 new
构造函数是惯用的做法,但包含其他方便的构造函数也很常见。
这使得向量的构造变得不那么嘈杂:
let funcs: Vec<Box<dyn Function>> = vec!(Add::new_boxed(), Multiply::new_boxed()));
What is the takeaway with trait objects in this case?
使用动态调度总是会对性能造成小的影响。如果你所有的对象都是同一类型,它们可以密集地存放在内存中,这样迭代速度会快得多。一般来说,我不会太担心这个,除非你正在创建一个库箱,或者如果你真的想挤出最后一纳秒的性能。
我是 Rust 的新手,我正在尝试了解该语言的基础知识。
考虑以下特征
trait Function {
fn value(&self, arg: &[f64]) -> f64;
}
和两个实现它的结构:
struct Add {}
struct Multiply {}
impl Function for Add {
fn value(&self, arg: &[f64]) -> f64 {
arg[0] + arg[1]
}
}
impl Function for Multiply {
fn value(&self, arg: &[f64]) -> f64 {
arg[0] * arg[1]
}
}
在我的 main()
函数中,我想将 Add
和 Multiply
的两个实例分组到一个向量中,然后调用 value
方法。以下作品:
fn main() {
let x = vec![1.0, 2.0];
let funcs: Vec<&dyn Function> = vec![&Add {}, &Multiply {}];
for f in funcs {
println!("{}", f.value(&x));
}
}
还有:
fn main() {
let x = vec![1.0, 2.0];
let funcs: Vec<Box<dyn Function>> = vec![Box::new(Add {}), Box::new(Multiply {})];
for f in funcs {
println!("{}", f.value(&x));
}
}
有没有更好/更简洁的方法?我可以解决将实例包装在 Box
中的问题吗?在这种情况下,特征对象的要点是什么?
Is there any better / less verbose way?
确实没有办法使它不那么冗长。由于您使用的是特征对象,因此您需要告诉编译器向量的项是 dyn Function
而不是具体类型。编译器不能仅仅推断出您指的是 dyn Function
特征对象,因为 Add
和 Multiply
都可以实现其他特征。
您也无法抽象出对 Box::new
的调用。为此,您必须以某种方式映射异构集合,这在 Rust 中是不可能的。但是,如果你经常写这个,你可以考虑为每个具体的 impl
:
impl Add {
fn new() -> Add {
Add {}
}
fn new_boxed() -> Box<Add> {
Box::new(Add::new())
}
}
尽可能包含 new
构造函数是惯用的做法,但包含其他方便的构造函数也很常见。
这使得向量的构造变得不那么嘈杂:
let funcs: Vec<Box<dyn Function>> = vec!(Add::new_boxed(), Multiply::new_boxed()));
What is the takeaway with trait objects in this case?
使用动态调度总是会对性能造成小的影响。如果你所有的对象都是同一类型,它们可以密集地存放在内存中,这样迭代速度会快得多。一般来说,我不会太担心这个,除非你正在创建一个库箱,或者如果你真的想挤出最后一纳秒的性能。