提问人:Eleanor 提问时间:11/17/2023 最后编辑:E_net4Eleanor 更新时间:11/17/2023 访问量:111
如何在 Rust 中实现以下 OOP 模式?
How to implement the below OOP patterns in Rust?
问:
在阅读了 Rust 这本书之后,我仍然在思考 Rust 中以下 Python 代码的 OOP 实现:
from abc import abstractmethod
class Animal:
def __init__(self, name, age):
self.age = age
self.name = name
@abstractmethod
def eat(self):
pass
class Dog(Animal):
def __init__(self, name, age, favorite_bone):
super().__init__(name, age)
self.favorite_bone=favorite_bone
def eat(self):
print(f"Eating {self.favorite_bone}")
dog = Dog("Tom", 1, "big bone")
dog.eat()
该方法可以通过为 实现的 来完成,但随后我也希望从字段的 OOP 继承中受益。(请注意,我举了一个非常简单的例子,但在实践中手头的问题有更多的领域和方法)。eat
trait
Dog
name
age
这就是我到目前为止所做的:
trait Animal {
fn new(name: String, age: u8) -> Self;
fn eat(&self);
}
struct Dog {
name: String,
age: u8,
favorite_bone: String,
}
impl Animal for Dog {
fn new(name: String, age: u8) -> Dog {
Dog {
name,
age,
favorite_bone: String::new(),
}
}
fn eat(&self) {
println!("Eating {}", self.favorite_bone);
}
}
fn main() {
let dog = Dog {
name: String::from("Tom"),
age: 1,
favorite_bone: String::from("big bone"),
};
dog.eat();
}
我的主要问题是,对于所有实现该特征的人,我需要重新定义方法,其中包含许多类似的设置逻辑,(并且实际上还有更多字段)。有没有更好的方法?struct
Animal
new
name
age
答:
这个想法是更喜欢组合而不是继承(不仅在 Rust 中)。 结构包含要实现的概念的公共部分,以及指向动态部分的一个(或多个)指针,该部分可以以 OOP 方式更改。 这样的动态部分通常是实现接口的盒装对象(Rust 中的一个特征)。
在下面的示例中,我决定让该方法使动物发生变异,以说明微妙之处。
事实上,我们不能调用,因为该参数是独占引用,尽管它在调用开始时使用。
该技巧依赖于对保存动态行为的选项的获取/重新分配序列。.eat()
self.eating_behaviour.eat(self)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pub mod abstract_level {
pub struct Animal {
name: String,
weight: u8,
eating_behaviour: Option<Box<dyn EatingBehaviour>>,
}
impl Animal {
pub fn new(
name: String,
weight: u8,
eating_behaviour: Box<dyn EatingBehaviour>,
) -> Self {
Self {
name,
weight,
eating_behaviour: Some(eating_behaviour),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn weight(&self) -> u8 {
self.weight
}
pub fn increase_weight(
&mut self,
delta: u8,
) {
self.weight += delta
}
pub fn eat(&mut self) {
// take/reassing in or dor to avoid multiple exclusive refs
if let Some(eating_behaviour) = self.eating_behaviour.take() {
eating_behaviour.eat(self);
self.eating_behaviour = Some(eating_behaviour);
}
}
}
pub trait EatingBehaviour {
fn eat(
&self,
me: &mut Animal,
);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pub mod dog_specific {
use super::abstract_level::{Animal, EatingBehaviour};
struct DogEatingBehaviour {
favorite_bone: String,
}
impl DogEatingBehaviour {
fn new(favorite_bone: String) -> Self {
Self { favorite_bone }
}
}
impl EatingBehaviour for DogEatingBehaviour {
fn eat(
&self,
me: &mut Animal,
) {
println!("dog eating {}", self.favorite_bone);
me.increase_weight(2);
}
}
// for convenience
pub fn make_dog(
name: String,
weight: u8,
favorite_bone: String,
) -> Animal {
Animal::new(
name,
weight,
Box::new(DogEatingBehaviour::new(favorite_bone)),
)
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
pub mod cat_specific {
use super::abstract_level::{Animal, EatingBehaviour};
struct CatEatingBehaviour {}
impl CatEatingBehaviour {
fn new() -> Self {
Self {}
}
}
impl EatingBehaviour for CatEatingBehaviour {
fn eat(
&self,
me: &mut Animal,
) {
println!("cat eating");
me.increase_weight(1);
}
}
// for convenience
pub fn make_cat(
name: String,
weight: u8,
) -> Animal {
Animal::new(name, weight, Box::new(CatEatingBehaviour::new()))
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
fn main() {
let mut animals = [
dog_specific::make_dog("Tom".to_owned(), 10, "big bone".to_owned()),
cat_specific::make_cat("Hop".to_owned(), 3),
];
for a in animals.iter_mut() {
println!("~~~~~~~~");
println!("{}, {} kg", a.name(), a.weight());
a.eat();
println!("{}, {} kg", a.name(), a.weight());
}
}
/*
~~~~~~~~
Tom, 10 kg
dog eating big bone
Tom, 12 kg
~~~~~~~~
Hop, 3 kg
cat eating
Hop, 4 kg
*/
虽然结构之间不能有继承,但你可以让结构实现相同的特征。由于您的 Python 代码包含两个类,因此您需要两个结构体和两个类的特征,而不仅仅是一个特征/结构,就像您的实现一样。
若要从基类借用实现中获益,只需委托而不是继承。
有多种方法可以做到这一点,无论你做什么,都要确保你与 Rust 关于所有者实例和拥有实例之间生命周期依赖关系的要求同步。
但只是为了给你一个接近你的 Python 代码的例子:
trait Creature {
fn eat(&self) -> ();
}
struct Animal {
age: i32
}
impl Animal {
fn new(age: i32) -> Self {
Self { age }
}
}
impl Creature for Animal {
fn eat(&self) {
println!("Animal::eats, age: {:?}", self.age );
}
}
struct Dog {
a: Animal,
bone: i32
}
impl Dog {
fn new(age: i32, bone: i32) -> Self {
Self { a: Animal::new(age), bone }
}
}
impl Creature for Dog {
fn eat(&self) {
println!("Dog::eats, age: {:?}, bone: {:?}", self.a.age, self.bone );
}
}
fn main() {
let animal = Animal::new(1);
animal.eat();
let dog = Dog::new(1, 6);
dog.eat();
}
请注意,这里唯一的委托是针对数据(委托给 ),但如果 不仅打印,而且返回某些内容,则委托结构 () 可以使用它委托调用的实例,调用方法并在自己的环境中使用它。age
a
eat
Dog
eat
评论
Animal
struct Dog{ animal: Animal, favorite_bone: String }
Animal
Eater