Struct inheritance - rust

A typical AST implementation in C might look like:
typedef enum {
AST_IDENT,
AST_STRING,
} Ast_Tag;
typedef struct { Ast_Tag tag; u32 flags; } Ast;
typedef struct { Ast base; Intern *name; } Ast_Ident;
typedef struct { Ast base; Intern *str; } Ast_String;
// A function that downcasts to the derived type:
void foo (Ast *node) {
switch (node->tag) {
case AST_IDENT: {
Ast_Ident *ident = (Ast_Ident*)node;
do_something_with_name(ident->name);
} break;
case AST_STRING: {
Ast_String *str = (Ast_String*)node;
do_something_with_name(str->name);
} break;
}
}
// A function that upcasts to the base type:
void bar (Ast_Ident *ident) {
foo((Ast*)ident);
}
Is there a way to do this in rust? I'd imagine that downcasting would be particularly problematic.
NOTE: I'm not asking how to implement an AST, but how to replicate the struct inheritance as demonstrated above.
EDIT: Sorry for the confusion. The point here is that struct inheritance is used in order to avoid making every node be the same size, so enums are not an option.

You basically implement Rust enum variant:
struct Ast {
flags: u32,
kind: AstKind,
}
enum AstKind {
Ident(String),
String(String),
}
fn foo(ast: Ast) {
match ast.kind {
AstKind::Ident(ident) => println!("flags: {:?}, ident: {:?}", ast.flags, ident),
AstKind::String(s) => println!("string: {:?}, ident: {:?}", ast.flags, s),
}
}
fn bar(flags: u32, ident: String) {
foo(Ast {
flags,
kind: AstKind::Ident(ident),
})
}
fn main() {
bar(42, "MisterMV".into())
}
You seem to be concern with the size:
The point here is that struct inheritance is used in order to avoid making every node be the same size, so enums are not an option.
That generally not a concern, clippy have a lint call large_enum_variant, with a reasonable default that you can change. Clippy also propose a reasonable solution:
enum AstKind {
Ident(Box<Big>),
String(Small),
}
There are other alternatives that other answer already cover.

You can reference multiple types as a trait, but you can't cast the trait object to the exact type. To the rescue is the Any trait, which has a is, downcast_ref, and Box<dyn Any> has a downcast method. You can also get type IDs.
But there is no inheritance like you see in many OOP languages. That goal is accomplished by implementing traits.

That's impossible in safe Rust. You can however do it with transmute, provided that your structs are all repr(C):
pub enum AstTag {
AstIdent,
AstString,
}
#[repr(C)]
pub struct Ast { tag: AstTag, flags: u32, }
#[repr(C)]
pub struct AstIdent { base: Ast, name: String, }
#[repr(C)]
pub struct AstString { base: Ast, string: String, }
// A function that downcasts to the derived type:
pub fn foo (node: &Ast) {
use std::mem::transmute;
match node.tag {
AstTag::AstIdent => {
let ident: &AstIdent = unsafe { transmute (node) };
println!("{}", ident.name);
},
AstTag::AstString => {
let ident: &AstString = unsafe { transmute (node) };
println!("{}", ident.string);
}
}
}
// A function that upcasts to the base type:
pub fn bar (ident: &AstIdent) {
foo (&ident.base);
}
Playground
However this is very unidiomatic. A much more idiomatic way of doing things would be to put foo in a trait and implement that trait for each struct:
pub struct AstIdent { flags: u32, name: String, }
pub struct AstString { flags: u32, string: String, }
pub trait Foo {
fn foo (&self);
}
impl Foo for AstIdent {
fn foo (&self) {
println!("{}", self.name);
}
}
impl Foo for AstString {
fn foo (&self) {
println!("{}", self.string);
}
}
pub fn bar (ident: &AstIdent) {
ident.foo();
}
Playground

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

Convert enum variants with named data into separate structs using procedural macros

I'm writing a procedural macro to convert the variants of an enum into individual structs and implement some traits for that struct.
This works fine for unit and unnamed variants, but variants with named data will cause it to silently fail :).
Here is an example proc_macro definition:
extern crate proc_macro;
use quote::ToTokens;
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DataEnum, DeriveInput};
#[proc_macro_derive(MyProcMacro)]
pub fn derive_my_proc_macro(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
// Error out if we're not annotating an enum
let data: DataEnum = match ast.data {
Data::Enum(d) => d,
_ => panic!("My structs can only be derived for enums"),
};
let variants = data.variants.iter();
let variant_structs = variants.map(|v| {
let var_id = &v.ident;
let fields = v.fields.clone().into_token_stream();
quote! {
pub struct #var_id #fields;
/* Implement traits for the new struct and stuff */
}
});
let gen = quote! {
#(#variant_structs)*
};
gen.into()
}
When I run it on this code:
#[derive(MyProcMacro)]
enum AllTypesOfVariants {
Unit,
OneUnNamed(bool),
MultiUnNamed(bool, bool, bool),
Named { _thing: bool },
MultiNamed { _thing: bool, _thing2: bool },
}
I get this expanded code (via cargo expand):
pub struct Unit;
pub struct OneUnNamed(bool);
pub struct MultiUnNamed(bool, bool, bool);
pub struct Named {
_thing: bool,
}
The expected result however would be:
pub struct Unit;
pub struct OneUnNamed(bool);
pub struct MultiUnNamed(bool, bool, bool);
pub struct Named {
_thing: bool,
}
pub struct MultiNamed {
_thing: bool,
_thing2: bool
}
The problem is in the semi-colon in the quote!().
Structs with unnamed fields should be terminated with a semicolon:
pub struct MultiUnNamed(bool, bool, bool);
But structs with named fields shouldn't:
pub struct MultiNamed {
_thing: bool,
_thing2: bool
}
The problem was solved by replacing:
quote! {
pub struct #var_id #fields;
}
with
match &v.fields {
Fields::Named(_) => {
quote! {
pub struct #var_id #fields
}
},
_ => {
quote! {
pub struct #var_id #fields;
}
}
}
(Also had to import syn::Fields)

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`"
}

Rust extend struct fields? [duplicate]

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

Resources