Rust extend struct fields? [duplicate] - struct

Using rust 1.2.0
Problem
I'm still in the process of learning Rust (coming from a Javascript background) and am trying to figure out if it is possible for one struct StructB to extend an existing struct StructA such that StructB has all the 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;
}
}
Constraints
StructA is from an external cargo package that I have no control over.
Current Progress
I found this blog post on single-inheritance which sounds like exactly what I need.
But trying to implement it resulted in this error message error: virtual structs have been removed from the language. Some searching later and I found out that it had been implemented and then removed per RFC-341 rather quickly.
Also found this thread about using traits, but since StructA is from an external cargo package I don't think it is possible for me to turn it into a trait.
So what would be the correct way to accomplish this in Rust?

There is nothing that exactly matches that. 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 embed one struct into another. The memory layout is nice and compact, but you have to manually delegate all the methods from Person to Child or lend out a &Person.
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.
As DK. mentions, you could choose to implement Deref or DerefMut. However, I do not agree that these traits should be used in this manner. My argument is akin to the argument that using classical object-oriented inheritance simply for code reuse is the wrong thing. "Favor composition over inheritance" => "favor composition over Deref". However, I do hold out hope for a language feature that enables succinct delegation, reducing the annoyance of composition.

Rust does not have struct inheritance of any kind. If you want StructB to contain the same fields as StructA, then you need to use composition.
struct StructB {
a: StructA,
// other fields...
}
Also, to clarify, traits are only able to define methods and associated types; they cannot define fields.
If you want to be able to use a StructB as a StructA, you can get some of the way there by implementing the Deref and DerefMut traits, which will allow the compiler to implicitly cast pointers to StructBs to pointers to StructAs:
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());
}

Another alternative is to use generics:
trait IAnimalData {}
struct Animal<D: IAnimalData> {
name: String,
age: i64,
child_data: D,
}
struct Dog {
favorite_toy: String,
}
impl IAnimalData for Dog {}
And then you can implement "child" methods like this, which will only apply to dogs:
impl Animal<Dog> {
pub fn bark(&self) -> String {
return "bark!".to_owned();
}
}
And if you want parent methods that apply to all animals, you can implement them like this:
// implements the 'breathe' method for all animals
impl<T: IAnimalData> Animal<T> {
fn breathe() {}
}
The good part is that you don't have to go through the pain of forwarding methods in Dog to methods in Animal; you can use them directly inside impl Animal<Dog>. Also, you can access any fields defined in Animal from any method of Animal<Dog>. The bad part is that your inheritance chain is always visible (that is, you will probably never use Dog in your code, but rather Animal<Dog>). Also, if the inheritance chain is long, you might get some very silly, long-winded types, like Animal<Dog<Chihuahua>>. I guess at that point a type alias would be advisable.

One thing that is good to mention for new corners in Rust is the way to design your structs/classes/traits. Try to keep your traits small and simple.
And take advantage of possible to use multiples traits for the same class:
trait Animal {
fn print_name(&self);
}
trait Attack {
fn can_kick(&self) -> bool {
false
}
}
trait Behavior {
fn can_fly(&self) -> bool {
true
}
}
impl Aminal for Bird {}
impl Behavior for Bird {}
impl Attack for Bird {}

Ok, the answer is very simple. Use macro.
macro_rules! Person {
(#[derive($($derive:meta),*)] $pub:vis struct $name:ident { $($fpub:vis $field:ident : $type:ty,)* }) => {
#[derive($($derive),*)]
$pub struct $name {
// required for all persones
age: i64,
$($fpub $field : $type,)*
}
impl $name {
$pub fn new(age:i64,$($field:$type,)*) -> Self{
Self{
age,
$($field,)*
}
}
}
}
}
And then use it like this:
Person! {
pub stuct Kid {
extra_field: String,
}
}

Related

How can I inherit methods for a struct containing another one

I am from C++ background and very new to rust. I have the following code which I have written to understand the code reusability, which is the sole purpose behind inheritance.
/* in animal.rs */
//declare Animal here
pub struct Animal;
// implement Animal here
impl Animal
{
pub fn breathe(&self)
{
println!("All animals breathe!");
}
}
// declare Dog here
pub struct Dog
{
pub parent: Animal
}
// implement Dog here
impl Dog
{
pub fn bark(&self)
{
println!("Dogs bark!");
}
pub fn breathe(&self)
{
self.parent.breathe();
}
}
/* in main.rs */
mod animal;
fn main()
{
let d = animal::Dog {parent : animal::Animal};
d.bark();
d.breathe();
}
If I don't implement the breathe function in Dog the compiler doesn't find it. In C++ the child inherits the functions from its parent. Can someone explain it?
Additional question: Can someone show some example code how dynamic/late binding works in rust. In C++ it is achieved by virtual functions.
There is no struct inheritance in Rust but you can use trait like in the following example to define a shared behavior. And you can see dyn AnimalTrait keyword which is used for dynamic dispatch in Rust.
trait AnimalTrait {
fn is_canine(&self) -> String {
return String::from("canine");
}
}
pub struct Dog;
impl AnimalTrait for Dog {}
pub struct Cat;
impl AnimalTrait for Cat {}
pub struct Mouse;
impl AnimalTrait for Mouse {
fn is_canine(&self) -> String {
return String::from("not canine");
}
}
fn main() {
let (d, c, m) = (Dog {}, Cat {}, Mouse {});
let all_animals: Vec<Box<dyn AnimalTrait>> = vec![Box::new(d), Box::new(c), Box::new(m)];
for e in all_animals {
match e.is_canine().as_str() {
"canine" => println!("I can tear apart well."),
"not canine" => println!("I will have hard time tearing apart."),
_ => (),
}
}
}

Extending On An SO Answered Question About Strategy Pattern Implementation

As answered by the ever so helpful and ubiquitous Shepmaster, could someone help me with a syntax hurdle I'm encountering?
In the previous answer, Strategy::execute() and Context::do_things() returns ().
How does one implement if a generic type is returned? Or am I missing some fundamental perspective in Rust?
I tried the following code but am currently stuck at:
struct Context<S> {
strategy: S,
}
impl<S> Context<S>
where
S: Strategy,
{
fn do_things(&self) -> T {
println!("Common preamble");
self.strategy.execute()
}
}
trait Strategy<T> {
fn execute(&self) -> T;
}
struct ConcreteStrategyA;
impl Strategy<AStruct> for ConcreteStrategyA {
fn execute(&self) -> AStruct {
println!("ConcreteStrategyA");
AStruct::default()
}
}
struct AStruct {
id: u32,
}
impl Default for AStruct {
...
}
struct ConcreteStrategyB;
impl Strategy<BStruct> for ConcreteStrategyB {
fn execute(&self) -> BStruct {
println!("ConcreteStrategyB");
BStruct::default()
}
}
struct BStruct {
id: u32,
}
impl Default for BStruct {
...
}
I have no idea where to put T for Context::do_things() -> T.
I looked around but some other samples return () as well.
Online tinkering
Thanks for reading.
Depending on what you're trying to do, it might be better to use an associated type instead of a generic. When choosing between associated types and generic parameters, you should use a generic if the caller can choose the type to use. You should use an associated type if the implementation determines the type.
Although there are exceptions (e.g. Into::into), most of the time if a type appears only in the return of a method, then this is a good indication that it should probably be an associated type. OTOH if a type is used for a parameter, then there is a fair chance that it should be a generic.
In your case, using an associated type would look like this:
struct Context<S> {
strategy: S,
}
impl<S> Context<S>
where
S: Strategy,
{
fn do_things(&self) -> S::Output {
println!("Common preamble");
self.strategy.execute()
}
}
trait Strategy {
type Output;
fn execute(&self) -> Self::Output;
}
struct ConcreteStrategyA;
impl Strategy for ConcreteStrategyA {
type Output = AStruct;
fn execute(&self) -> AStruct {
println!("ConcreteStrategyA");
AStruct::default()
}
}
#[derive (Default)]
struct AStruct {
id: u32,
}
struct ConcreteStrategyB;
impl Strategy for ConcreteStrategyB {
type Output = BStruct;
fn execute(&self) -> BStruct {
println!("ConcreteStrategyB");
BStruct::default()
}
}
#[derive (Default)]
struct BStruct {
id: u32,
}
Playground
I think using Strategy<T> more often is the key:
impl<S, T> Context<S, T>
where
S: Strategy<T>,
{
fn do_things(&self) -> T {
println!("Common preamble");
self.strategy.execute()
}
}
See here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a3a14895e22ca57f5f96c855b7046257

How to avoid exhaustive matching and directly return value of enum instead?

I got two structs (Dog and Cat) which implements the same trait Animal.
#![allow(unused)]
pub struct Dog;
pub struct Cat;
trait Animal {
fn walk();
}
impl Animal for Dog {
fn walk() {
println!("Dog walking.");
}
}
impl Animal for Cat {
fn walk() {
println!("Cat walking.");
}
}
Then I have this function, which accepts a generic T type as a parameter. The generic type should implement the trait Animal and execute the method from the specified struct.
fn walk_module<T: Animal>(animal: T) {
T::walk();
}
I hope to use this like so.
fn main() {
walk_module(Cat); // prints Cat walking.
}
So everything is good, the animal is walking.
Playground here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7c16dfbf651b0b5871e771e7b64d17fb
However, the issue starts when I try to fetch the structs from an enum.
-- snips --
enum AnimalCategory {
Dog(Dog),
Cat(Cat)
}
fn get_the_animal(category: &str) -> AnimalCategory {
match category {
"dog" => AnimalCategory::Dog(Dog),
"cat" => AnimalCategory::Cat(Cat),
_ => AnimalCategory::Dog(Dog),
}
}
fn main() {
// Option 1
let animal = get_the_animal("dog");
walk_module(animal); // this errors now because enum AnimalCategory does not implement the Animal trait, and so the animal can't walk
}
I do not want to implement the Animal trait for AnimalCategory, but I know that I could implement methods for the enum AnimalCategory and have it return the correct variant and do exhaustive matching but it seems redundant, and I am not sure if I am doing it correctly.
-- snips --
impl AnimalCategory {
fn dog(self) -> Dog {
if let AnimalCategory::Dog(d) = self { d } else { panic!("Not dog!") }
}
fn cat(self) -> Cat {
if let AnimalCategory::Cat(c) = self { c } else { panic!("Not cat!") }
}
}
-- snips --
fn main() {
// OPTION 2
let animal = get_the_animal("dog");
if let AnimalCategory::Dog(Dog) = animal {
let dog = animal.dog();
walk_module(dog);
cry_module(dog); // this is where the redundancy comes
} else {
let cat = animal.cat();
walk_module(cat);
cry_module(cat); // this is where the redundancy comes
}
}
If I have to implement new methods, then I have to call the function/module twice. I would like to be able to do the OPTION 1 instead, but I could not figure it out.
I do not want to implement the Animal trait for Animal category
Why?
and I am not sure if I am doing it correctly.
I don't think that is the correct solution no.
As far as I'm concerned, implementing Animal for AnimalCategory sounds like a fine solution. An alternative would be to leverage dynamic dispatch but this requires a few changes:
Your trait currently is not object-safe, which means it can not be used for dynamic dispatch. The solution is simple enough: make it operate on instances:
trait Animal {
fn walk(&self);
}
impl Animal for Dog {
fn walk(&self) {
println!("Dog walking.");
}
}
impl Animal for Cat {
fn walk(&self) {
println!("Cat walking.");
}
}
this does require updating walk_module to work on the instance as well:
fn walk_module<T: Animal>(animal: T) {
animal.walk();
}
Next you need a cast method: if you have an AnimalCategory you should have a way to get an Animal out of it, without knowing which animal specifically:
impl AnimalCategory {
fn as_animal(&self) -> &dyn Animal {
match self {
Self::Dog(d) => d,
Self::Cat(c) => c,
}
}
}
now we can get our animal:
let animal = get_the_animal("dog");
walk_module(animal.as_animal());
however there is still an issue: &dyn Trait does not implement Trait by default, so we need to add one such implementation in order for our &dyn Animal to be an Animal. That's easy enough as we can just delegate to the underlying object (dyn Trait does implement Trait)
impl Animal for &dyn Animal {
fn walk(&self) { (*self).walk() }
}
et voilĂ .
Incidentally in the second example the retrieval methods are completely unnecessary, you can already get the instances from matching the enum:
fn main() {
let animal = get_the_animal("dog");
match animal {
AnimalCategory::Dog(dog) => {
walk_module(dog);
}
AnimalCategory::Cat(cat) => {
walk_module(cat);
}
}
}

How can I denote a field that can be either Rc<T> or Weak<T>

I'd like to have a field in struct like this:
struct Foo<T> {
bar: Smart<T>
}
where bar could be either Rc<T or Weak<T>, depending on the "ownership relationships" between different instances of Foo. Is there any idiomatic way in Rust how to do this, other than to create a custom enum?
Is there any idiomatic way in Rust how to do this, other than to create a custom enum?
Just like most other "either this or that" choices in Rust, an enum is the idiomatic way.
An Peter's answer suggested, there is is no such abstraction in the stdlib. You can define a small enum to handle both cases:
use std::rc::{Rc, Weak};
enum MaybeStrong<T> {
Strong(Rc<T>),
Weak(Weak<T>),
}
impl<T> MaybeStrong<T> {
fn get(&self) -> Option<Rc<T>> {
match self {
MaybeStrong::Strong(t) => Some(Rc::clone(t)),
MaybeStrong::Weak(w) => w.upgrade(),
}
}
}
struct Foo<T> {
bar: MaybeStrong<T>
}
impl<T> Foo<T> {
fn from_weak(inner: Weak<T>) -> Self {
Self { bar: MaybeStrong::Weak(inner) }
}
fn from_strong(inner: Rc<T>) -> Self {
Self { bar: MaybeStrong::Strong(inner) }
}
fn say(&self) where T: std::fmt::Debug {
println!("{:?}", self.bar.get())
}
}
fn main() {
let inner = Rc::new("foo!");
Foo::from_weak(Rc::downgrade(&inner)).say();
Foo::from_strong(inner).say();
}
self.bar() will always return a Some if it was created from a strong pointer and return None in case it's a Weak and it's dangling. Notice that due to the fact that get() needs to create an owned Rc first, the method can't return a &T (including Option<&T>) because that &T could be dangling. This also means that all users of bar() will own one strong count on the inner value while processing, making it safe to use in any case.
This kind of construct is often called "Either" and there's a crate that looks like it can address some of the usual use-cases: https://docs.rs/either/1.5.3/either/
Then you could write
struct Foo<T> {
bar: Either<Weak<T>, Rc<T>>
}
Then an example function to get an Option<Rc<T>> might be:
impl <T> Foo<T> {
fn get_rc(self) -> Option<Rc<T>> {
self.bar
.map_left( |weak| weak.upgrade() )
.map_right( |v| Some(v) )
.into_inner()
}
}
Then it can be used like this:
fn main() {
let x = Rc::new(1);
let f_direct = Foo{ bar:Either::Right(x.clone()) };
println!("f_direct.get_rc() = {:?}", f_direct.get_rc());
let f_weak = Foo{ bar:Either::Left(Rc::downgrade(&x)) };
println!("f_weak.get_rc() = {:?}", f_weak.get_rc());
}
Link to complete example in the playground:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c20faaa46277550e16a3d3b24f3d1750

How do I 'force' structs to implement the same traits?

I have the following:
pub struct OpBStruct {
title: String,
output_vale: i32,
}
impl OpBStruct {
pub fn new_OpB(in_title: String, in_output_vale: i32) -> OpBStruct {
OpBStruct {
title: in_title,
output_vale: in_output_vale,
}
}
}
pub struct OpCStruct {
title: String,
another_value: String,
output_vale: i32,
}
impl OpCStruct {
pub fn new_OpC(in_title: String, in_another_value: String, in_output_vale: i32) -> OpCStruct {
OpCStruct {
title: in_title,
another_value: in_another_value,
output_vale: in_output_vale,
}
}
}
impl A {
pub fn new_A(in_name: String, in_operator: Op) -> A {
A {
name: in_name,
operator: in_operator,
}
}
}
pub enum Op {
OpB(OpBStruct),
OpC(OpCStruct),
}
pub struct A {
name: String,
operator: Op,
}
impl A {
pub fn new_A(in_name: String, in_operator: Op) -> A {
A {
name: in_name,
operator: in_operator,
}
}
}
The exact structure of OpBStruct and OpCStruct are arbitrary and could be anything.
How do I make sure OpBStruct and OpCStruct implement a certain trait?
trait OpTrait {
pub fn get_op_output(&self) -> i32;
}
I thought about making a sort of constructor function that checked for an OpTrait trait requirement and it would be the only way one could create an Op instance, but each operator requires different initialization parameters and there's no way to specify a variable number of inputs for a function in Rust.
Something like this doesn't work because there's no way to input the initialization parameters:
pub fn new_op<T: OpTrait>(operator: T) {
// --snip--
}
I thought about somehow using the new_A method implemented on A to check if the in_operator has implemented the trait, but I'm not sure how to do that either.
What is the correct pattern for this? If there is none, I can just implement the trait for each Op with no sort of interface around it.
I would also recommend writing a test, however you can write a function which is generic over a type but takes no arguments:
struct X {}
trait Y {
fn yo();
}
fn is_y<T: Y>(){}
Then you can add the following line to do the check
is_y::<X>();
which will compile only if X implements Y.
Using a unit test would be a fairly straightforward way to enforce that you want a given trait on a struct. You can do it via implicit test code, but a small utility function to do so can express the intent a little more clearly.
If you have indicated trait inputs on functions in the rest of the code, it might come out fairly naturally without the unit test. The test has the advantage of letting you have an opportunity to explicitly check some invariant of the trait implementation too.
struct A {
val: u8,
}
struct B {
val: u32,
}
trait ExpandToU64 {
fn to_u64(&self) -> u64;
}
impl ExpandToU64 for A {
fn to_u64(&self) -> u64
{
self.val as u64
}
}
fn trait_tester<E>(a: E)
where E: ExpandToU64
{
// the utility function doesn't have to even use the trait...
// but you probably want to exercise the logic a bit
//let v = a.to_u64();
let v = 24u64;
println!("{:?}", v);
}
#[test]
fn test_needs_trait_ExpandToU64() {
let a = A { val:1 };
trait_tester(a);
let b = B { val:2 };
trait_tester(b);
// This fails with a compile error
// "the trait `ExpandToU64` is not implemented for `B`"
}

Resources