Is it possible for one structure to expand the existing structure, retaining all the fields?

Using rust 1.2.0

Problem

I'm still learning Rust (based on Javascript background) and trying to figure out if it is possible for a single StructB structure StructB extend the existing StructA structure StructA that StructB all fields defined on StructA .

In Javascript (ES6 syntax), I could essentially do something like this ...

 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; } } 

Limitations

  • StructA is an external cargo package that I do not control.

Current progress

I found this single inheritance blog post that sounds like exactly what I need.

But an attempt to implement it led to this error message error: virtual structs have been removed from the language . Some searches later, and I found out that it was implemented and then deleted in RFC-341 pretty quickly.

This thread was also found on the use of signs , but since StructA from an external load package, I do not think it is possible for me to turn it into a dash.

So what would be the right way to do this in Rust?

+11
source share
3 answers

There is nothing that exactly matches this. There are two concepts that come to mind.

  • Structural composition

     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()); } 

    You can simply insert one structure into another. The memory layout is nice and compact, but you must manually delegate all methods from Person to Child or borrow &Person .

  • Character traits

     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); } 

You can combine these two concepts, of course.


Like DK. mentions , you can choose Deref or DerefMut . However, I do not agree that these traits should be used in this way. My argument is akin to the argument that using classic object-oriented inheritance just to reuse code is the wrong thing. "Use composition over inheritance" => "approve composition over Deref ". However, I really hope for a language feature that allows concise delegation, while reducing irritation to the composition.

+16
source

Rust does not have structure inheritance of any type. If you want StructB contain the same fields as StructA , you need to use composition.

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

In addition, to clarify, traits can only define methods and their associated types; they cannot define fields.

If you want to use StructB as StructA , you can get part of this path by running Deref and DerefMut , which allows the compiler to indirectly point to pointers to StructB to pointers to 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()); } 
+11
source

Deref implementation for smart pointers that we can access the data behind, sort of like inheriting.

 struct Person { gender: &'static str, age: &'static i32 } struct Child(Person); impl std::ops::Deref for Child { type Target = Person; fn deref(&self) -> &Self -> &Self::Target { &self.0 } } fn main() { let child = Child(Person{ gender: 'male', age: 6_i32 }); println!("your child is {}, {} years old.", child.gender, child.age); } 
0
source

All Articles