Ensure that struct T has size n, at compile time?

pub struct T { ... }

#[test]
fn test_00() {
  assert!( std::mem::size_of::<T>() == n);
}

I want the code to run only if struct T has size n.

Is there a way to force either (1) a check at compile time or (2) always check at runtime (even if I forget to put it in fn main()) ?

1 Like

You can check it by creating a constant byte array:

struct Test { val: i32 }

// compiles
const TestChecker: [u8; 4] = [0; std::mem::size_of::<Test>()];

// doesn't compile
// const TestChecker: [u8; 6] = [0; std::mem::size_of::<Test>()];
10 Likes

This is brilliant!

1 Like

More minimal version:

const _: () = [(); 1][(core::mem::size_of::<T>() == n) as usize ^ 1];
5 Likes

How does this work?

"... == n" returns true => index = 0 => works fine
"... == n" returns false => index = 1 => bound error

Does this have the "weakness" where if we could somehow tell the compiler to ignore bound checks, then this fails ?

At it's core, it's just a silly workaround for the fact that rust doesn't allow panicking in the const context at the moment: https://github.com/rust-lang/rust/issues/85194. So, this is just a horrible fact to write if !cond { panic!() }. It weaponizes the fact that, although custom panics don't work in consts, build-in panics are allowed. One of the build-in panics is on index out of bounds (Rust guarantees that out of bounds accesses always trap). Another option is division by zero:

const _: u8 = 0 / (cond as u8);
1 Like

The static-assertions crate has an assert_eq_size! macro for this.

4 Likes

Note that when doing these things, the quality of the error message varies from one approach to another. For "same size types", @Cerber-Ursi's suggestion, or the assert_eq_size! macro are the ones, imho, yielding the better ones (it will given an "expected n, got m" kind of error message, vs. "Any usage of this const will fail: index out of bounds, the len is 0 but got 1".

1 Like

To illustrate, here's an example error from assert_eq_size!:

error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
 --> src/main.rs:3:1
  |
3 | static_assertions::assert_eq_size!(u32, MyType);
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: source type: `u32` (32 bits)
  = note: target type: `MyType` (64 bits)
2 Likes

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.