Given x: TX with TX: Foo, I can get MEMBER either by doing TX::MEMBER or x.get_member(). This is good for non-const contexts.
Say I have a macro (or, really, any context where I can only get an opaque value as-is) that takes a value of a type implementing Foo. I want to perform a static check on the value of MEMBER:
macro_rules! mac {
($e:expr) => {
const CHECK: () = assert!($e::MEMBER != 0); // sic
}
}
The above doesn’t work, because :: must be used on a type. I can’t use get_member (even if it were a const fn) either because then I’d get error[E0435]: attempt to use a non-constant value in a constant.
I’ve thought about inference tricks but they all end up “using” $e in a way that counts as E0435.
If Rust had C’s typeof (which is const even for non-const operands, since they’re unevaluated) I could do typeof($e)::MEMBER, but it doesn’t.
Is there any way, in Rust (assume latest nightly, any unstable feature you want) to do this? To recap: getting a const member of the type of a non-const value in a const context.
Well, like the prophecy said, I found the answer about 30 seconds before posting this, even though I’ve been looking for an answer for days. So this is an auto-answer.
You don’t need TAIT or any unstable features. You can use an “inference trick” that doesn’t attempt to actually const evaluate the expression (unless the macro is itself called from a const context). The key is to call a generic function (which can therefore have a name T for the type and refer to T::MEMBER) in a way which could evaluate the expression, but in fact never does.
If you’re going to define assert_helper anew in each macro call, you should put it in a block so it doesn’t pollute the outer scope. (But I would avoid that entirely, and use impls on tuples to make it close enough to variadic. Macros should generate as little code as possible, to reduce the cost of compiling their outputs.)