如何共享相同的实现以及共享字段

问题描述 投票:0回答:3

如何简化这段代码?在面向对象编程之后,我仍然无法理解 Rust 的特征和结构。

struct Player {
    entity: Entity,
    hp: i32,
    atk: i32
}

struct Chest {
    entity: Entity,
    amount: i32
}

impl Drawable for Chest {
    fn draw(&self, mut pencil: Pencil) {
        pencil.draw(&self.entity);
    }
}

impl Drawable for Player {
    fn draw(&self, mut pencil: Pencil) {
        pencil.draw(&self.entity);
    }
}

也许有一种方法可以像 OOP 中那样继承某些字段?

另外,如果有人知道有关 Rust 特征和结构的良好且清晰的教程,如果您分享它,我将非常高兴!

struct rust traits
3个回答
5
投票

根据我的经验,通常当您想要像这样“共享属性”时,您通常希望将结构分解为自己的类型并在每个结构上实现您需要的特征。

考虑一下你的结构是否看起来像这样:

struct DrawableEntity {
    entity: Entity,
    ... // other stuff you might need to draw
}

struct Chest {
    drawable: DrawableEntity,
    ...
}

struct Player {
    drawable: DrawableEntity,
    ...
}

impl Drawable for DrawableEntity { ... }

那么在你的代码中它可能看起来像这样:

player.drawable.draw(mut pencil);
chest.drawable.draw(mut pencil);

1
投票

由于您的

impl
块使用不同的类型执行完全相同的操作,因此您可以将其抽象为宏,这样对于任何新的
Drawable
类型重复此操作都很简单:

macro_rules! impl_drawable {
    ($t:ty) => {
        impl Drawable for $t {
            fn draw(&self, mut pencil: Pencil) {
                pencil.draw(&self.entity);
            }
        }
    };
}

impl_drawable!(Chest);
impl_drawable!(Player);

然而,这就是我认为你能做的一切,特征不能像 OOP 语言中的抽象类那样拥有字段,所以你不能真正“分享”在结构中拥有

Entity
的想法。

如果您还没有读过本文,这本书有一章讨论了一些常见的 OOP 模式以及它们如何转换为惯用的 Rust。


0
投票

我们可以在 Rust 书的第 16.2 章“使用 dyn 返回特征”中找到示例。

这里是一些基于问题的示例代码。我将带有问题答案的函数放在文件顶部,以便新手更容易阅读。

fn main() {
//Initialise game
//New empty struct
let pencil = Pencil {}; 

//We need Box <dyn TraitName> because Drawable can be: Player or a Chest, here it's a player
let player: Box<dyn Drawable> = create_player_or_chest(true);

//We need Box <dyn TraitName> because Drawable can be: Player or a Chest, here it's a chest
let chest: Box<dyn Drawable> = create_player_or_chest(false);

//Play game
player.draw(&pencil);
chest.draw(&pencil);

}

fn create_player_or_chest(is_player:bool) -> Box<dyn Drawable > {
    if is_player {
        println!("GAME INIT: Creating a new player");
        Box::new(Player {
            hp: 100,
            atk: 2,
            entity: Entity {
                name: String::from("Mage")
            }
        })
    } else {
        println!("GAME INIT: Creating a new chest");
        Box::new(Chest {
            amount: 1,
            entity: Entity {
                name: String::from("Withering staff")
            }
        })
    }
}

impl Drawable for Chest {
    fn draw(&self, pencil: &Pencil) {
        println!("Drawing chest with amount of items: {}", self.amount);
        pencil.draw(&self.entity);
    }
}

impl Drawable for Player {
    fn draw(&self, pencil:&Pencil) {
        println!("Drawing player with hp: {} and atk: {}", self.hp, self.atk);
        pencil.draw(&self.entity);
    }
}

impl Pencil {
    fn draw(&self, entity: &Entity) {
        println!("{}", entity.name);
    }
}

trait Drawable {
    fn draw(&self, pencil: &Pencil);
}
struct Entity {
    name: String,
}

struct Player {
    entity: Entity,
    hp: i32,
    atk: i32
}

struct Chest {
    entity: Entity,
    amount: i32
}

struct Pencil {}
© www.soinside.com 2019 - 2024. All rights reserved.