Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to automatically define fields of a struct?

Tags:

I'm using a macro to implement a trait as part of my library. This implementation requires the struct to have at least one additional field.

pub trait Trait {
    fn access_var(&mut self, var: bool);
}

macro_rules! impl_trait {
    (for $struct:ident) => {
        impl Trait for $struct {
            pub fn access_var(&mut self, var: bool) {
                self.var = var; // requires self to have a field 'var'
            }
        }
    }
}

I want to prevent the user from having to add those additional fields every time. Due to the fact that the Rust compiler does not allow macros in field definitions (I have no source for this, so please correct me if I'm wrong), something like this does not work.

macro_rules! variables_for_trait {
    () => {
        var: bool,
    }   
};

struct Foo {
    variables_for_trait!(); // error: expected ':' found '!'
    additional_var: i64,
}

I guess that I could create a macro which enables something like this

bar!(Foo with additional_var: i64, other_var: u64);

to look like that after resolving the macro:

pub struct Foo {
   var: bool,
   additional_var: i64,
   other_var: u64,
}

impl Trait for Foo {
   pub fn access_var(&mut self, var: bool) {
        self.var = var;
   }
}

Is there a better way to solve this, and if not, can you give me an example syntax for bar!?

P.S: What would be a good name for something like bar!?

like image 566
lncr Avatar asked Jul 21 '17 08:07

lncr


1 Answers

I ended up solving the problem with 2 different macros:

// I simply copied it from my project and changed some names,
// But due to the fact that I couldn't think of better names for my macros,
// I just ended up using the original names, even though they don't quite fit

macro_rules! new_object_type {
    ($struct:ident {$( $field:ident:$type:ty ),*}) =>{
        pub struct $struct {
            var: bool,
            $(
                $field: $type,
            )*
        }

        impl Trait for $struct {
            pub fn access_var(&mut self, var: bool) {
                self.var = var;
            }
        }
    };
}

macro_rules! construct_object {
    ($struct:ident {$( $field:ident:$value:expr ),*}) => {
        $struct {
           var: false,
           $(
               $field: $value, 
           )*
       }
    };
}

To create a new struct implementing Trait you now write:

new_object_type!(Foo {
    additional_var: i64,
    other_var: u64,
});

And to create a new instance of Foo you write:

construct_object!(Foo {
    additional_var: 42,
    other_var: 82000,
})

Now it is possible to use Trait without ever having to interact with var.

Problems with this approach:

  • It is not as clean as I hoped it would be and without proper documentation it is hard to use, especially if the user is fairly new to Rust.

  • There might be some problem with having two identical fields, as the user does not see all implemented variables (This could be solved by changing the name of var to something like T7dkD3S3O8 which would most certainly make it quite unlikely that this ever happens)

  • As the definition and the construction of the struct are both inside a macro, error messages could be harder to understand

like image 74
lncr Avatar answered Sep 28 '22 05:09

lncr