Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to access the type of a struct member for function signatures or declarations?

Tags:

When defining implementations in macros, it might be useful to access a struct members type to avoid having to pass it as an extra argument. (see this question)

impl PartialEq<u32> for MyStruct { ... }

Is there a way to access the type of a struct member without knowing in advance which type it is?

impl PartialEq<typeof(MyStruct.member)> for MyStruct { ... }

In case it's helpful, this is an abbreviated example of why I'm interested to do this:

struct_bitflag_impl!(
    pub struct MyFlag(u8);,
    MyFlag, u8);

//          ^^ how to avoid having this extra arg?
//             (Used by ``impl PartialEq<$t_internal> for $p``)
//             couldn't it be discovered from `MyFlag.0` ?

// the macro

macro_rules! struct_bitflag_impl {
    ($struct_p_def: item, $p:ident, $t_internal:ty) => {

        #[derive(PartialEq, Eq, Copy, Clone, Debug)]
        $struct_p_def

        impl ::std::ops::BitAnd for $p {
            type Output = $p;
            fn bitand(self, _rhs: $p) -> $p { $p(self.0 & _rhs.0) }
        }
        impl ::std::ops::BitOr for $p {
            type Output = $p;
            fn bitor(self, _rhs: $p) -> $p { $p(self.0 | _rhs.0) }
        }
        impl ::std::ops::BitXor for $p {
            type Output = $p;
            fn bitxor(self, _rhs: $p) -> $p { $p(self.0 ^ _rhs.0) }
        }

        impl ::std::ops::Not for $p {
            type Output = $p;
            fn not(self) -> $p { $p(!self.0) }
        }

        // support comparison with the base-type.
        impl PartialEq<$t_internal> for $p {
            fn eq(&self, other: &t_internal) -> bool {
                self.0 == *other
            }
        }
        // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        //       How to avoid using 't_internal' here?
    }
}
like image 322
ideasman42 Avatar asked Nov 13 '16 10:11

ideasman42


1 Answers

No, a general typeof(Type::field) that can be used in type position does not exist.


Regarding the example in the question, it looks like you are expecting a special kind of item: a tuple-struct with only one field. So, instead of accepting an $item fragment, you can simulate the syntax yourself:

macro_rules! foo {
    (pub struct $name:ident ( $ty:ident ) ) => {
        pub struct $name($ty);

        impl $name {
            pub fn bar() { 
                println!("{}", stringify!($ty)); 
            }
        }
    }
}

foo!(
    pub struct Peter(u8)
);

fn main() {
    Peter::bar();
}

That way you only have to specify everything once. However, this obviously only works with one kind of tuple-struct definition, not all kinds of items. But your use case suggests that you are more or less only interested in this special case.

If you want to allow different kinds of struct definitions, you just need to add more macro-rules to the macro to allow for different syntax. To see an example, here is code to allow for pub and non-pub tuple-struct definitions. But this could be expanded even more.

like image 184
Lukas Kalbertodt Avatar answered Sep 25 '22 16:09

Lukas Kalbertodt