0

I have a derive style procedural macro, where i would like to calculate the potential maximum length of the serialized version of a struct. For example, given the struct TestStruct below, i would like to call some function with all the fields to calculate the worst case length.

#[derive(MyLengthProcMacro)]
struct TestStruct {
    f1: u32,
    f2: i64,
    f3: SomeOtherStruct
}

For primitives this could look something like:

fn get_string_length(ty: &Type) -> usize {
    let type_string = quote! { #ty }.to_string();
    match type_string.replace(" ", "").as_str() {
        "str" => panic!("String slices must be annotated with a length using #[at_arg()]"),
        "tuple" => panic!("Tuples are not supported!"),
        "char" => "a".len(),
        "bool" => "false".len(),
        "isize" => format!("{:?}", std::isize::MAX).len(),
        "usize" => format!("{:?}", std::usize::MAX).len(),
        "u8" => format!("{:?}", std::u8::MAX).len(),
        "u16" => format!("{:?}", std::u16::MAX).len(),
        "u32" => format!("{:?}", std::u32::MAX).len(),
        "u64" => format!("{:?}", std::u64::MAX).len(),
        "u128" => format!("{:?}", std::u128::MAX).len(),
        "i8" => format!("{:?}", std::i8::MIN).len(),
        "i16" => format!("{:?}", std::i16::MIN).len(),
        "i32" => format!("{:?}", std::i32::MIN).len(),
        "i64" => format!("{:?}", std::i64::MIN).len(),
        "i128" => format!("{:?}", std::i128::MIN).len(),
        "f32" => format!("{:?}", std::f32::MIN).len(),
        "f64" => format!("{:?}", std::f64::MIN).len(),
        _ => {
            // println!("Unexpected type: {:?}", type_string);
            0
        }
    }
}

The problem i am asking here is regarding any non-primitive fields, eg. f3: SomeOtherStruct. Is there a way to access the fields of fields, in a proc-macro?

smilykoch
  • 57
  • 8

1 Answers1

2

No.

Macros are expanded before that kind of analysis is done, so the compiler has no idea what SomeOtherStruct is at that point.


However, this is not how you should implement that macro! Your way doesn't allow the user to use type aliases.

What you should do is simply use your trait recursively and use sum the <T as MyLength>::my_length() where T is the type of the field for each field.

They manually implement your trait for the primitive types.

mcarton
  • 27,633
  • 5
  • 85
  • 95
  • Thanks for the answer.. Seems like what i want cannot be done.. The issue is that the "length" i am trying to calculate is a [generic_array](https://docs.rs/generic-array/0.14.1/generic_array/) length, so its a stack allocated array, where the length must be known at compile time, thus the proc_macro to make a worst-case estimate. – smilykoch Apr 21 '20 at 11:45
  • @smilykoch It doesn't change anything: [use a trait with an associated constant](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f9db6b8bd28c1b967a37a847c3b55eb6). – mcarton Apr 21 '20 at 11:53
  • Ahhhh! That way! Thank you SOO much! that is such an elegant way. – smilykoch Apr 21 '20 at 12:27