0

Since Rust does not support inheritance, we cannot reuse the states of another struct.

Take an example in Head First Design Patterns, an abstract Duck class has an attribute FlyBehavior, and it also provides getter and setter.

abstract class Duck {
   private FlyBehavior flyBehavior;
   public void setFlyBehavior(FlyBehavior flyBehavior) { this.flyBehavior = flyBehavior; }
   public FlyBehavior getFlyBehavior() { return this.flyBehavior; }
}

But, in Rust, we are not able to write the default implementation for reuse.

trait Duck {
    fn get_fly_behavior(&self) -> &dyn FlyBehavior;
    fn set_fly_behavior(&mut self, fly_behavior: Box<dyn FlyBehavior>);
}

And any struct implementing Duck has to write the same getter and setter. So, is there any idiomatic way to reuse the implementations of getter and setter in Rust?

chenzhongpu
  • 6,193
  • 8
  • 41
  • 79
  • 2
    This doesn't answer your question, but let me give you a general piece of advice: don't try to apply OOP design patterns to Rust. Most of them don't map to Rust at all and are not useful in Rust. For example, getter/setter pairs are rather rare in most Rust code. I know it's hard to unlearn things, but Rust has a fairly different way of thinking about problems than OOP does. If you want, you could explain the general problem you're trying to solve on the Rust forum and ask how one would solve it in idiomatic Rust. I think that might be more helpful than an answer to this exact question. – Lukas Kalbertodt May 26 '21 at 07:34
  • Also, you almost certainly want `FlyBehavior` to be an enum. Making it a trait almost seems like an intentional parody of badly mapping of OOP to Rust. (And please don't take this remark as unfriendly, the use of dynamic dispatch for what is conceptually an enum just looks super-weird in Rust.) – user4815162342 May 26 '21 at 08:09
  • A Struct is not a class, a struct is a struct. Your trait can use other methods within the trait. So you can 'get' and 'set' the field which you want to edit as a method. And then do more complex things through other methods. But there are very likely better design patterns. – Gibus Jun 19 '22 at 07:44

2 Answers2

0

You can reuse the trait u have posted by providing the field fly_behavior in the structs you want to implement the trait for, as the following demo ensures

trait FlyBehavior {
    fn fun(&self);
}

trait Duck {
    fn get_fly_behavior(&self) -> &dyn FlyBehavior;
    fn set_fly_behavior(&mut self, fly_behavior: Box<dyn FlyBehavior>);
}

struct Behavior1{}
struct Behavior2{}
struct DuckObject {
    fly_behavior: Box<dyn FlyBehavior>,
}
impl Duck for DuckObject {
    fn get_fly_behavior(&self) -> &dyn FlyBehavior{
        &*self.fly_behavior
    }
    fn set_fly_behavior(&mut self, fly_behavior: Box<dyn FlyBehavior>){
        self.fly_behavior=fly_behavior
    }
}
impl FlyBehavior for Behavior1{
    fn fun(&self) {
        println!("Behave1")
    }
}

impl FlyBehavior for Behavior2{
    fn fun(&self) {
        println!("Behave2")
    }
}
fn main() {
    let mut duck = DuckObject{ fly_behavior: Box::new(Behavior1{}) };
    duck.get_fly_behavior().fun();
    duck.set_fly_behavior(Box::new(Behavior2{}));
    duck.get_fly_behavior().fun();
}

The result

asmmo
  • 6,922
  • 1
  • 11
  • 25
  • You may not get my point. Now every struct implementing `Duck` still has to write the same get/set code. For example, what if a `DuckObject2`? Then `impl Duck for DuckObject2` will **repeat** all you have written. – chenzhongpu May 26 '21 at 04:42
  • @chenzhongpu I think the point in the question is you couldn't reuse the trait or u couldn't implement it. but if u want to avoid the repetition, use the procedural macros. like what's done by using `#[derive(std::fmt::Debug)]` for example – asmmo May 26 '21 at 04:48
  • Thanks for the hints. I think it is a little inconvenient to write design patterns (for example, `strategy` in the example) since Rust does not support inheritance. So, I wonder what the best practice is for this case in Rust. – chenzhongpu May 26 '21 at 04:54
0

You can use a macro_rule to reuse your getter/setter implementation, which is quite simple to write because we just need it to paste exact same code everywhere.

Say you have a struct and trait like below:

trait Test {
    fn get_a(&self) -> i32;
    fn get_b(&self) -> i32;
}
struct TT {
    a: i32,
    b: i32,
}

All you need to do is this:

macro_rules! getters {
    {} => {
        fn get_a(&self) -> i32 {
            self.a
        }
        fn get_b(&self) -> i32 {
            self.b
        }
    }
}

Then you can spam the getters!(); macro inside the impl block for any struct implementing Test trait.

Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d897baa26b8c32e5e7bde6c596714353

wujj123456
  • 529
  • 4
  • 8