简体   繁体   中英

Rust trait state

I'll start with this monster "Monster" code from Rust for Rubyist :

trait Monster {
    fn attack(&self);
    fn new() -> Self;
}

struct IndustrialRaverMonkey {
    life: int,
    strength: int,
    charisma: int,
    weapon: int,
}

struct DwarvenAngel {
    life: int,
    strength: int,
    charisma: int,
    weapon: int,
} ...
impl Monster for IndustrialRaverMonkey { ...
impl Monster for DwarvenAngel { ...

I worry about duplication of code. In Java I would create interface that define attack method and base class with all that parameters ( life , strength , charisma , weapon ). I will do same thing in C++ with abstract class. I can find some ugly and unintuitive ways to work around this problem, but is there a good way to reduce code? I mean, to keep it scalable and readable.

Another approach, which favors composition, and from which is easier to diverge implementations if needed (eg, the Characteristics for DwarvenAngel require an additional field):

trait Monster {
    fn attack(&self);
}

struct Characteristics {
    life: int,
    strength: int,
    charisma: int,
    weapon: int,
}

struct IndustrialRaverMonkey {
    characteristics: Characteristics
}

struct DwarvenAngel {
    characteristics: Characteristics
}

fn same_attack(c: Characteristics) {
    fail!("not implemented")
}

impl Monster for IndustrialRaverMonkey {
    fn attack(&self) {
        same_attack(self.characteristics)
    }
}

impl Monster for DwarvenAngel {
    fn attack(&self) {
        same_attack(self.characteristics)
    }
}

Or, you can have an enum represent your monster types, very similar to AB's answer:

trait Monster {
    fn attack(&self);
}

struct Characteristics {
    life: int,
    strength: int,
    charisma: int,
    weapon: int,
}

enum Monsters {
    IndustrialRaverMonkey(Characteristics),
    DwarvenAngel(Characteristics),
}

fn same_attack(_: &Characteristics) {
    fail!("not implemented")
}

impl Monster for Monsters {
    fn attack(&self) {
        match *self {
            IndustrialRaverMonkey(ref c) => same_attack(c),
            DwarvenAngel(ref c)          => same_attack(c)
        }
    }
}

Would you consider something like this an acceptable solution?

trait Actor {
    fn attack(&self);
}

enum MonsterId {
    IndustrialRaverMonkey,
    DwarvenAngel
}

struct Monster {
    life: int,
    strength: int
}

impl Monster {
    fn new(id: MonsterId) -> Monster {
        match id {
            IndustrialRaverMonkey => 
            Monster { life: 12, strength: 8 },

            DwarvenAngel => 
            Monster { life: 18, strength: 12 }
        }
    }
}


impl Actor for Monster { 
    fn attack(&self) {}
}

Updated with a better example.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM