Can't use type parameters of outer function, but only in constant expression

Hi all,

I have a function that I want to execute only if the size of two types T and U is the same. I can write

fn do_magic<T,U>(t: T, u: U) {
  assert!(std::mem::size_of::<T>()==std::mem::size_of::<U>());
  // ...
}

This works, but since the size is compile time known, I would like to fail at compile time. So I did this nifty trick to make this a compile time assertion:

fn do_magic<T,U>(t: T, u: U) {
  const _ : () = assert!(core::mem::size_of::<T>() == core::mem::size_of::<U>());
  // ...
}

and this makes the compiler tell me can't use generic parameters from outer function. Why is it that it only gets upset in the constant-evaluated context. This seems like it's a misleading error message because to my mind I have been using the generic parameters from the outer function in the first case as well...

Is there any way around this problem?

Currently items (like const, static, struct, enum, fn etc etc) are kinda global. Even if you define them inside some scope (like in this case you're defining a const inside the scope of a function) the definition will still be as if you defined it outside, except the name is only visible inside the scope you defined it.

So your code is equivalent to this one:

const _ : () = assert!(core::mem::size_of::<T>() == core::mem::size_of::<U>());
fn do_magic<T,U>(t: T, u: U) {
    // ...
}

and it's clear here that T and U are not available to the definition of the const.

This doesn't happen with just assert, because it's a statement, not an item.

You can work around this problem by using an associated const because those have access to the generic parameters of the impl, but it's not very intuitive and is a bit boilerplaty:

fn do_magic<T,U>(t: T, u: U) {
    struct Helper<A, B>(A, B);
    impl<A, B> Helper<A, B> {
        // Here A and B are available to the associated const
        const ASSERTION: () = assert!(core::mem::size_of::<A>() == core::mem::size_of::<B>());
    }
    // Force the monomorphization of the constant
    let _ = Helper::<T, U>::ASSERTION;
    // ...
}

In the future you might be able to make the const directly generic over A and B and then force the monomorphization, which should reduce a bit the boilerplate.

5 Likes

wow, I was not aware this was the case. The way you circumvented that limitation was very crafty, thank you!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.