articles

New rust lint: function_casts_as_integer

I recently added a new lint (emitting a warning by default) named function_casts_as_integer in the rust compiler when a function pointer is cast as a pointer in #141470. Let's dive into the reasons why it was added and what we plan to do next.

Why?

In rust, it's possible to do:

Runlet x = u32::max as usize;

Which is fine... until you realize that u32::max is a function, not a constant (the constant is named u32::MAX). And it can become catastrophic in cases like:

Runlet x = [0; u32::max as usize];

Or:

Runif x.len() > u32::max as usize {
    // ...
}

I chose specifically this second example because it was the root issue of CVE-2025-1014, which happened in the Firefox web browser (reported here) to demonstrate that this issue actually has "real world" impact.

Suggested fix

Now let's take a look at what the suggested fix looks like. So this code:

Runlet x = u32::max as usize;

will suggest:

warning: direct cast of function item into an integer
 --> src/main.rs:3:18
  |
3 | let x = u32::max as usize;
  |                  ^^^^^^^^
  |
  = note: `#[warn(function_casts_as_integer)]` on by default
help: first cast to a pointer `as *const ()`
  |
3 | let x = u32::max as *const () as usize;
  |                  ++++++++++++

So the "fixed" code would look like this:

Runlet x = u32::max as *const () as usize;

Having the as * const () part to be present in the code will force code readers to wonder why such a cast is there for what appears to be an integer cast. In short: to make it stand out to prevent it to go unnoticed.

However, we're not completely satisfied with this because, although it attracts readers attention on this part of the code, it's not really clear what's happening just from reading it. An alternative was to force having the function declaration in the cast:

Runlet x = u32::max as *const fn() -> u32 as usize;

However, we are planning to have another approach in the future to cast function pointers to integers. So until then, we decided to keep the simpler approach.

What's coming next

We're planning to create two new traits: FnPtr and FnStatic. The one we're interested into here is FnPtr because it would add an as_ptr method, directly callable on functions/methods. So the previous suggested code would become:

Runlet x = u32::max.as_ptr() as usize;

Once this new trait has been implemented and stabilized, this is what the lint will suggest.

You can follow progress of these new traits implementations by taking a look at their tracking issue.

A bit of history

Because it's always interesting to know how something comes to be, I decided to add this section:

It all started from a clippy lint named confusing_method_to_numeric_cast (implemented in this PR) which checked special cases like:

Runlet _ = u32::max as usize;

It's suggesting to replace it with the actual constant:

Runlet _ = u32::MAX as usize;

However, when we realized that it might need to grow a lot more when we added cases like <integer>::max_value or <integer>::maximum, we decided that having something more general directly in the rust compiler would be preferable.

Words of the end

Today I'm making an exception as I couldn't decide which pic I preferred, so double cat dose!


my cat in a tree cleaning up himself
my cat in a tree looking at something behind photograph
Posted on the 28/11/2025 at 12:00 by @GuillaumeGomez

Previous article

Rust: Optimizing integer to string conversions
Back to articles list
RSS feedRSS feed