#Confusing "generic parameter from outer scope" error in declarative macro

41 messages · Page 1 of 1 (latest)

dusty dock
#

To get a deeper understanding of how macros work in Rust I decided to replicate a macro from a C library.

During this, I needed to create an array of integers storing the size of each field.

My initial attempt looked like:

macro_rules! sample {
    ($($lit:literal, $ty:ty,)+) => {
        const COUNT: usize = 0 $(+ {$lit; 1})* + 0;
        const SIZES: [usize; COUNT] = {
            let sizes = [$(size_of::<$ty>(),)*];
            sizes
        };
    };
}

fn myfunc<'a>() {
    sample!(1, u32, 2, &'a str,);
}

This expanded to what I expected it to

const COUNT: usize =
    0 + {
        1;
        1
    } + {
        2;
        1
    } + 0;
const SIZES: [usize; COUNT] = {
    let sizes = [size_of::<u32>(), size_of::<&'a str>()];
    sizes
};

But this gave an error:

error[E0401]: can't use generic parameters from outer item
  --> src/lib.rs:12:25
   |
11 | fn asd<'a>() {
   |        -- lifetime parameter from outer item
12 |     sample!(1, u32, 2, &'a str,);
   |                         ^^ use of generic parameter from outer item

So I thought that I just have to remove the lifetime from the size_of call and all should be good.

I added another macro to shave off the lifetime from the reference but then I kept getting the same error.
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=9dda7d1117a29ce541acd46536219552
Expanded (which compiles just fine when copied over):

const COUNT: usize =
    0 + {
        1;
        1
    } + {
        2;
        1
    } + 0;
const SIZES: [usize; COUNT] = {
    let sizes = [size_of::<u32>(), size_of::<&str>()];
    sizes
};

This actually isn't exactly what I am trying to do and I have already found other ways to do what I want.
But it this got me curious on why this is causes an error as I can't really see anything wrong with the expanded code and it compiles fine when inlined.
Is there some magic I'm not understanding here?

#

Oh and replacing &'a str with &'static str instead of &str didn't help either.

placid garden
keen caveBOT
placid garden
#

?

#

And your lf_remover doesn't work because you matched a ty

#

And you can't split that

dusty dock
dusty dock
placid garden
#

Since rustc has to find a place to point to

dusty dock
#

I am aware that macro errors can end up weird but I wasn't expecting this tbh.

#

Thanks for the response rustOk

dusty dock
placid garden
#

?play ```rs
macro_rules! sample {
($($ty:ty),+) => {
const COUNT: usize = 0 $(+ {None::<$ty>; 1});
const SIZES: [usize; COUNT] = {
let sizes = [$(size_of::<$ty>(),)
];
sizes
};
};
}

fn myfunc<'a>() {
sample!(u32, &'static str);
}

keen caveBOT
placid garden
#

The macro wasn't doing anything

#

In any of them

#

Also your COUNT variable is ... Not right

dusty dock
#

I know there are better ways, this just seemed the simplest for the snippet.

dusty dock
#

The 'static is even removed from the final result.

placid garden
placid garden
# dusty dock The `'static` is even removed from the final result.

?expand ```rs
macro_rules! sample {
($($lit:literal, $ty:ty),+) => {
const COUNT: usize = 0 $(+ {$lit; 1})* + 0;
const SIZES: [usize; COUNT] = {
let sizes = [$(size_of::<lf_remover!($ty)>(),)*];
sizes
};
};
}

macro_rules! lf_remover {
(&$lf:lifetime $tt:tt) => {
&$tt
};
($tt:tt) => {
$tt
}
}

fn myfunc<'a>() {
sample!(1, u32, 2, &'static str);
}

keen caveBOT
#
macro_rules! sample {
    ($($lit:literal, $ty:ty),+) =>
    {
        const COUNT: usize = 0 $(+ { $lit; 1 })* + 0; const SIZES:
        [usize; COUNT] =
        { let sizes = [$(size_of::<lf_remover!($ty)>(),)*]; sizes };
    };
}

macro_rules! lf_remover {
    (&$lf:lifetime $tt:tt) => {
        &$tt
    };
    ($tt:tt) => {
        $tt
    };
}

fn myfunc<'a>() {
    const COUNT: usize =
        0 + {
            1;
            1
        } + {
            2;
            1
        } + 0;
    const SIZES: [usize; COUNT] = {
        let sizes = [size_of::<u32>(), size_of::<&'static str>()];
        sizes
    };
}```
dusty dock
#

odd

placid garden
#

As I said the lf_remover macro does nothing

dusty dock
#

locally i get this ```rust
const COUNT: usize =
0 + {
1;
1
} + {
2;
1
} + 0;
const SIZES: [usize; COUNT] = {
let sizes = [size_of::<u32>(), size_of::<&str>()];
sizes
};

placid garden
#

?play ```rs
macro_rules! sample {
($($ty:ty),+) => {
const COUNT: usize = 0 $(+ {None::<$ty>; 1});
const SIZES: [usize; COUNT] = {
let sizes = [$(size_of::<$ty>(),)
];
sizes
};
};
}

fn myfunc<'a>() {
sample!(u32, &str);
}

keen caveBOT
placid garden
#

Yes this does work

#

But it's not what the macro expands to

dusty dock
# placid garden What gave this to you
macro_rules! sample {
    ($($lit:literal, $ty:ty),+) => {
        const COUNT: usize = 0 $(+ {$lit; 1})* + 0;
        const SIZES: [usize; COUNT] = {
            let sizes = [$(size_of::<lf_remover!($ty)>(),)*];
            sizes
        };
    };
}

macro_rules! lf_remover {
    (&$lf:lifetime $tt:tt) => {
        &$tt
    };
    ($tt:tt) => {
        $tt
    }
}

pub fn myfunc<'a>() {
    sample!(1, u32, 2, &'static str);
}
#

I am using RustAnalyzers feature could that be a problem?

placid garden
# dusty dock ```rust macro_rules! sample { ($($lit:literal, $ty:ty),+) => { const...

?play ```rust
macro_rules! sample {
($($lit:literal, $ty:ty),+) => {
const COUNT: usize = 0 $(+ {$lit; 1})* + 0;
const SIZES: [usize; COUNT] = {
let sizes = [$(size_of::<lf_remover!($ty)>(),)*];
sizes
};
};
}

macro_rules! lf_remover {
(&$lf:lifetime $tt:tt) => {
explosion!!! ... why no explosion? :(
)};
($tt:tt) => {
$tt
}
}

pub fn myfunc<'a>() {
sample!(1, u32, 2, &'static str);
}

keen caveBOT
dusty dock
#

yeah definitely a rust analyzer problem, doesn't remove the lifetime when expanded via cargo

placid garden
#

Feel free to file an issue