Rust - 相同类型的不同行为
Rust - Same Type different behaviour
就我的理解而言,泛型允许在不同类型之间共享相同的行为。例如,
trait Bird {}
struct BirdFly {}
impl Bird for BirdFly {
pub fn fly() -> can fly
}
struct BirdCantFly {}
impl Bird for BirdCantFly{
pub fn fly() -> can't fly
}
let birds = vec![
Box::new(BirdFly{}), // allow this bird to fly, for instance
Box::new(BirdCantFly{}), // don't allow this bird to fly
];
我的问题是相反的,即是否可以让相同的类型采取不同的行为(没有 ifs、enums 或 Box)。在这个例子中,拥有一个 Box 似乎是一种浪费, BirdFly 和 BirdCantFly 在两种类型(BirdFly 和 BirdCantFly)中尺寸相同,只有行为不同。
类似于:
struct Bird {
fly: // associate different behavior
}
let birds = vec![
Bird{ fly: some_fly_behavior }, // allow this bird to fly, for instance
Bird{ fly: another_fly_behavior }, // don't allow this bird to fly
];
birds[0].fly();
birds[1].fly();
如果 fly 收到相同的参数和 returns 相同的类型,我看不出问题的原因。此外,通过这种方式我可以摆脱向量中的 Box。特别是因为我可能在向量中有数百万个元素并且被多次迭代访问,这样我就可以避免开销。
感谢您的帮助!
您可以将 function pointer 存储在 struct Bird
中并添加一个辅助方法来获取您想要的语法。
struct Bird {
name: String,
fly_impl: fn(&Bird) -> (),
}
impl Bird {
fn new(name: String, fly_impl: fn(&Bird) -> ()) -> Bird {
Bird { name, fly_impl }
}
fn fly(self: &Bird) {
(self.fly_impl)(self)
}
}
fn behavior1(b: &Bird) {
println!("{} behavior1", b.name);
}
fn behavior2(b: &Bird) {
println!("{} behavior2", b.name);
}
fn main() {
let captured_variable = 10;
let birds = vec![
Bird::new("Bird1".into(), behavior1),
Bird::new("Bird2".into(), behavior2),
Bird::new("Bird3".into(), |b| println!("{} lambda", b.name)),
/*Bird::new("Bird4".into(), |b| {
println!("{} lambda with {}", b.name, captured_variable)
}),*/
];
(birds[0].fly_impl)(&birds[0]);
for bird in birds {
bird.fly();
}
}
fn
不要与 trait Fn
混淆。前者仅允许函数和非捕获 lambda,并且具有固定大小。后者允许任意大小的任意捕获,因此需要像 Box
这样的间接寻址。两者都在调用时进行动态调度。
请注意 Bird3
的行为是如何由非捕获 lambda 指定的,这是允许的。
但是,如果您尝试取消注释 Bird4
,该示例将不会编译,因为它所需的行为是捕获 lambda,它通常可以增长任意大,我们已经禁止 Box
es 和其他间接寻址.
我对 Rust 的了解还不够多,无法告诉您是否有更像 Rust 的解决方案。但是,你的情况很具体:
- 您有一个任意可扩展的
Bird
层次结构。否则 enum
更合适。
- 但是,此层次结构中的所有
Bird
都具有完全相同的字段集和受支持的 trait
。否则,您必须通过 Box<dyn Bird>
. 使用 trait
s 和标准动态调度
- 您不想为单个
Bird
分配堆。否则,您可以使用 trait
s 和标准动态调度。
就我的理解而言,泛型允许在不同类型之间共享相同的行为。例如,
trait Bird {}
struct BirdFly {}
impl Bird for BirdFly {
pub fn fly() -> can fly
}
struct BirdCantFly {}
impl Bird for BirdCantFly{
pub fn fly() -> can't fly
}
let birds = vec![
Box::new(BirdFly{}), // allow this bird to fly, for instance
Box::new(BirdCantFly{}), // don't allow this bird to fly
];
我的问题是相反的,即是否可以让相同的类型采取不同的行为(没有 ifs、enums 或 Box)。在这个例子中,拥有一个 Box 似乎是一种浪费, BirdFly 和 BirdCantFly 在两种类型(BirdFly 和 BirdCantFly)中尺寸相同,只有行为不同。
类似于:
struct Bird {
fly: // associate different behavior
}
let birds = vec![
Bird{ fly: some_fly_behavior }, // allow this bird to fly, for instance
Bird{ fly: another_fly_behavior }, // don't allow this bird to fly
];
birds[0].fly();
birds[1].fly();
如果 fly 收到相同的参数和 returns 相同的类型,我看不出问题的原因。此外,通过这种方式我可以摆脱向量中的 Box。特别是因为我可能在向量中有数百万个元素并且被多次迭代访问,这样我就可以避免开销。
感谢您的帮助!
您可以将 function pointer 存储在 struct Bird
中并添加一个辅助方法来获取您想要的语法。
struct Bird {
name: String,
fly_impl: fn(&Bird) -> (),
}
impl Bird {
fn new(name: String, fly_impl: fn(&Bird) -> ()) -> Bird {
Bird { name, fly_impl }
}
fn fly(self: &Bird) {
(self.fly_impl)(self)
}
}
fn behavior1(b: &Bird) {
println!("{} behavior1", b.name);
}
fn behavior2(b: &Bird) {
println!("{} behavior2", b.name);
}
fn main() {
let captured_variable = 10;
let birds = vec![
Bird::new("Bird1".into(), behavior1),
Bird::new("Bird2".into(), behavior2),
Bird::new("Bird3".into(), |b| println!("{} lambda", b.name)),
/*Bird::new("Bird4".into(), |b| {
println!("{} lambda with {}", b.name, captured_variable)
}),*/
];
(birds[0].fly_impl)(&birds[0]);
for bird in birds {
bird.fly();
}
}
fn
不要与 trait Fn
混淆。前者仅允许函数和非捕获 lambda,并且具有固定大小。后者允许任意大小的任意捕获,因此需要像 Box
这样的间接寻址。两者都在调用时进行动态调度。
请注意 Bird3
的行为是如何由非捕获 lambda 指定的,这是允许的。
但是,如果您尝试取消注释 Bird4
,该示例将不会编译,因为它所需的行为是捕获 lambda,它通常可以增长任意大,我们已经禁止 Box
es 和其他间接寻址.
我对 Rust 的了解还不够多,无法告诉您是否有更像 Rust 的解决方案。但是,你的情况很具体:
- 您有一个任意可扩展的
Bird
层次结构。否则enum
更合适。 - 但是,此层次结构中的所有
Bird
都具有完全相同的字段集和受支持的trait
。否则,您必须通过Box<dyn Bird>
. 使用 - 您不想为单个
Bird
分配堆。否则,您可以使用trait
s 和标准动态调度。
trait
s 和标准动态调度