Inheritance 一个结构是否可以扩展现有结构,保留所有字段?

Inheritance 一个结构是否可以扩展现有结构,保留所有字段?,inheritance,struct,rust,Inheritance,Struct,Rust,使用rust 1.2.0 问题 我仍在学习Rust(来自Javascript背景)的过程中,我正在试图弄清楚一个structStructB是否可以扩展现有的structStructA,以便StructB拥有StructA上定义的所有字段 在Javascript(ES6语法)中,我基本上可以这样做 class Person { constructor (gender, age) { this.gender = gender; this.age = age;

使用
rust 1.2.0

问题

我仍在学习Rust(来自Javascript背景)的过程中,我正在试图弄清楚一个struct
StructB
是否可以扩展现有的struct
StructA
,以便
StructB
拥有
StructA
上定义的所有字段

在Javascript(ES6语法)中,我基本上可以这样做

class Person {
    constructor (gender, age) {
        this.gender = gender;
        this.age = age;
    }
}
class Child extends Person {
    constructor (name, gender, age) {
        super(gender, age);
        this.name = name;
    }
}
约束

  • StructA
    来自我无法控制的外部
    cargo
    包裹
当前进度

我发现这听起来正是我需要的

但是尝试实现它会导致以下错误消息
错误:虚拟结构已从语言中删除
。后来我搜索了一下,发现它已经很快被发现了

我也发现了这一点,但由于StructA是一个外部货物包装,我认为我不可能把它变成一个特征


那么,在Rust中实现这一点的正确方法是什么呢?

Rust没有任何类型的结构继承。如果希望
StructB
包含与
StructA
相同的字段,则需要使用组合

struct StructB {
    a: StructA,
    // other fields...
}
此外,为了澄清,特征只能定义方法和相关类型;它们不能定义字段

如果您想将
StructB
用作
StructA
,可以通过实现and traits来实现,这将允许编译器隐式地将指向
StructB
s的指针转换为指向
StructA
s的指针:

struct StructA;

impl StructA {
    fn name(&self) -> &'static str {
        "Anna"
    }
}

struct StructB {
    a: StructA, 
    // other fields...
}

impl std::ops::Deref for StructB {
    type Target = StructA;
    fn deref(&self) -> &Self::Target {
        &self.a
    }
}

fn main() {
    let b = StructB { a: StructA };
    println!("{}", b.name());
}

没有任何东西与此完全相符。我想到了两个概念

  • 结构组成

    struct Person {
        age: u8,
    }
    
    struct Child {
        person: Person,
        has_toy: bool,
    }
    
    impl Person {
        fn new(age: u8) -> Self {
            Person { age: age }
        }
    
        fn age(&self) -> u8 {
            self.age
        }
    }
    
    impl Child {
        fn new(age: u8, has_toy: bool) -> Self {
            Child { person: Person::new(age), has_toy: has_toy }
        }
    
        fn age(&self) -> u8 {
            self.person.age()
        }
    }
    
    fn main() {
        let p = Person::new(42);
        let c = Child::new(7, true);
    
        println!("I am {}", p.age());
        println!("My child is {}", c.age());
    }
    
    您可以简单地将一个结构嵌入到另一个结构中。内存布局美观紧凑,但您必须手动将所有方法从
    Person
    委派给
    Child
    或借出
    &Person

  • 特征

    trait SayHi {
        fn say_hi(&self);
    }
    
    struct Person {
        age: u8,
    }
    
    struct Child {
        age: u8,
        has_toy: bool,
    }
    
    impl SayHi for Person {
        fn say_hi(&self) {
            println!("Greetings. I am {}", self.age)
        }
    }
    
    impl SayHi for Child {
        fn say_hi(&self) {
            if self.has_toy {
                println!("I'm only {}, but I have a toy!", self.age)
            } else {
                println!("I'm only {}, and I don't even have a toy!", self.age)
            }
        }
    }
    
    fn greet<T>(thing: T)
        where T: SayHi
    {
        thing.say_hi()
    }
    
    fn main() {
        let p = Person { age: 42 };
        let c = Child { age: 7, has_toy: true };
    
        greet(p);
        greet(c);
    }
    
    trait SayHi{
    fn say__hi(&self);
    }
    结构人{
    年龄:u8,
    }
    结构子对象{
    年龄:u8,
    玩具:布尔,
    }
    为个人祈祷{
    fn say_hi(&self){
    println!(“你好,我是{}”,self.age)
    }
    }
    为孩子祈祷{
    fn say_hi(&self){
    如果赛尔夫有玩具{
    println!(“我只是{},但我有一个玩具!”,self.age)
    }否则{
    println!(“我只是{},甚至没有玩具!”,self.age)
    }
    }
    }
    fn问候(事物:T)
    T:SayHi在哪里
    {
    说你好
    }
    fn main(){
    设p=人{年龄:42};
    设c=Child{age:7,has_toy:true};
    问候(p);
    问候(c);
    }
    
  • 当然,您可以将这两个概念结合起来



    您可以选择实现
    Deref
    DerefMut
    。然而,我不同意以这种方式使用这些特性。我的观点类似于这样一种观点,即仅仅为了代码重用而使用经典的面向对象继承是错误的。“偏好组合而非继承”=>“偏好组合而非继承”。然而,我确实希望有一种语言特性,它能够实现简洁的委托,减少写作的麻烦。

    另一种选择是使用泛型:

    struct StructB {
        a: StructA,
        // other fields...
    }
    
    trait IAnimalData{}
    结构动物{
    名称:String,
    年龄:164岁,
    儿童资料:D,
    }
    结构狗{
    最喜欢的玩具:绳子,
    }
    狗{}的impl IAnimalData
    
    然后你可以实现这样的“child”方法,它只适用于狗:

    impl动物{
    pub fn bark(和self)->字符串{
    将“树皮!”返回给_owned();
    }
    }
    
    如果您希望父方法适用于所有动物,您可以这样实现它们:

    //为所有动物实现“呼吸”方法
    植入动物{
    fn呼吸(){}
    }
    

    好的方面是,你不必经历将
    Dog
    中的方法转发到
    Animal
    中的方法的痛苦;您可以直接在
    impl Animal
    中使用它们。此外,您还可以通过
    Animal
    的任何方法访问
    Animal
    中定义的任何字段。糟糕的是,您的继承链总是可见的(也就是说,您可能永远不会在代码中使用
    Dog
    ,而是使用
    Animal
    )。此外,如果继承链很长,您可能会得到一些非常愚蠢、冗长的类型,如
    Animal
    。我想在这一点上,最好使用类型别名。

    这正是我所怀疑的。感谢您的确认和快速回答!对于其他的人来说。。。这些代码片段是否为您编译?如果我没有
    new
    的返回类型,我会得到一个编译错误。@drebabels没有,这是我在传输时的错误。我确保它现在可以编译:-)@Shepmaster:为
    Child
    实现
    Deref
    DerefMut
    将大大有助于使组合方法很好地工作。因为在大多数情况下,你可以把一个
    孩子
    当作一个
    @chrismorge来对待,这可能是真的,但我属于那些认为这在语义上不正确的人。总有一天,我会写下我的RFC,提出一个替代方案。。。总有一天……这方面有什么消息吗?我认为在不知道您想要使用新生成的结构的上下文和原因的情况下回答这个问题总是次优的。在我看来,这些设计决策在很大程度上取决于特定的用例。这是一个很好的解决方案,但其他Rust程序员可能会嘲笑你命名traits
    ITraitName
    。只要
    TraitName
    就可以了。trait和类型不是同一名称空间的一部分,因此很少有任何混淆(至少,只要您对trait对象类型使用
    dyn
    。@trentcl“嘲笑您”是