Const Assertions
Oct 7, 2025 - ⧖ 3 minI'm currently working on a project which involves a lot of lower level data structures. By lower level I mean things like layout and bit positions, and exact sizes being important. As such, I have a number of pedantic lints enabled.
One of the lints I use is cast_precision_loss
.
For example, casting from usize
to f32
using the as
keyword is not guaranteed to be exact,
since f32
can only precisely represent integrals up to 23 bits of precision (due to how floating point is represented).
Above this you can have precision loss.
This lint is pedantic because it can generate false positives where you know the input can't ever exceed some threshold. But wouldn't it be nice if we could go from "knowing" we can safely disable a lint to actually proving it?
The first thing that came to mind was runtime assertions, but this is kind of ugly. It requires that we actually exercise the code at runtime, for one. We should be able to cover this in unit tests, but even if we do that, an assertion isn't as good as a compile time guarantee.
const
One thing I didn't mention, and the reason I "know" that the lint would be fine is that I'm using a const
declaration.
Here's a look at what that's like:
pub const BUCKET_SIZE_MINUTES: u32 = 5;
pub const BUCKETS_PER_WEEK: usize = (7 * 24 * 60) as usize / BUCKET_SIZE_MINUTES as usize;
This isn't the same as a static
or a let
binding.
const
expressions are actually evaluated at compile time.
(Well, most of the time... there's a funny edge case where const
blocks which can never executed at runtime
is not guaranteed to evaluate.)
You can't do everything in const
contexts, but you can do quite a lot, including many kinds of math.
Not all math; some things like square root and trigonometry are not yet usable in const
contexts
since they are not reproducible across architectures (and sometimes even on the same machine, it seems).
assert!
in a const
block
And now for the cool trick!
I want to do a division here, and to do so, I need to ensure the types match.
This involves casting a usize
to f32
,
which can cause truncation as noted above.
But since BUCKETS_PER_WEEK
is a constant value,
we can actually do an assertion against it in our const
context.
This lets us safely enable the lint, while ensuring we'll get a compile-time error if this ever changes!
This has no runtime overhead.
#[allow(clippy::cast_precision_loss, reason = "BUCKETS_PER_WEEK is always <= 23 bits")]
const PI_BUCKET_CONST: f32 = {
// Asserts the invariant; panics at compile time if violated
assert!(BUCKETS_PER_WEEK < 2usize.pow(24));
// Computes the value
std::f32::consts::PI / BUCKETS_PER_WEEK as f32
};
This is all possible in stable Rust at the time of this writing (tested on 1.89). I saw some older crates out there which appeared to do this, but as far as I can tell, they are no longer necessary.
Here's a Rust Playground
preloaded with the sample code
where you can verify that changing BUCKETS_PER_WEEK
to a disallowed value causes a compile-time error.