Search code examples
rustrust-macros

Calculating maximum value of a set of constant expressions at compile time


I'm trying to calculate the maximum value of a set of constants at compile time inside a Rust procedural macro (a derive macro).

The macro looks something like:

fn get_max_len() -> TokenStream {
    // Each TokenStream represents a constant expression
    let len: Vec<TokenStream> = get_constant_lengths();

    quote! {
      // #(#len),* gets expanded to #len[0], #len[1], #len[2]...
      const LEN: usize = std::cmp::max(#(#len),*);
    }
}

The problem is that std::cmp::max is a function and hence can't be used inside a constant expression (at least until const fn is stabilized - I want to keep to stable Rust if at all possible).

How can I calculate the max of a set of constants at compile-time?

I might be able to write a max! macro that basically constructs a huge chain of ifs recursively, but I'm hoping there is a cleaner solution out there.


Solution

  • While constant evaluation does not support if or other control flow, there is a way to select values depending on binary conditions:

    [a, b][(a < b) as usize]
    

    What this does is

    • create an array of the two elements you want to choose between
    • create an arbitrary boolean expression
    • cast said expresison to a usize
    • use that value to index into the array created above

    The first element is chosen if the condition is false, the second element is chosen if the condition is true.

    While this scheme can theoretically be extended to arbitrary length arrays by computing indices via mathematical operations on multiple casted bools, it seems simpler to just go the functional way and nest the above expression:

    const fn max(a: usize, b: usize) -> usize {
        [a, b][(a < b) as usize]
    }
    
    const MAX: usize = max(max(max(5, 6), 42), 3);
    

    Starting with Rust 1.31, const fn is usable on the stable compiler.