Imagine a Rust function that is always called with a special constant value. The compiler could optimize this by inlining the function, but this is not always possible.
With const
generics, we can instruct the compiler to create special versions of a function based on a constant. This can allow more compile-time optimizations.
In Rust, arrays must be specified with a constant size. But we can do this with a const
generic argument—this would allow us to reduce code repetition.
fn get_array<const C: usize>() -> [u8; C] { // Return an array of the specified size. let mut result = [0; C]; result[0] = 1; result } fn main() { println!("{:?}", get_array::<5>()); println!("{:?}", get_array::<6>()); }[1, 0, 0, 0, 0] [1, 0, 0, 0, 0, 0]
We specify a const
generic method by specifying the "const
" type and its name after the function name. Here the bool
N is the constant bool
type.
test()
function can have a true or false constant type. We specify this type when we call test()
in main.test_no_generic
function does the same thing as the test()
function, but it has no generic const
type.fn test<const N: bool>() { if N == true { println!("TRUE"); } else { println!("FALSE"); } } fn test_no_generic(n: bool) { if n { println!("TRUE"); } else { println!("FALSE"); } } fn main() { test::<true>(); test::<false>(); test_no_generic(true); test_no_generic(false); }TRUE FALSE TRUE FALSE
const
Can we get a performance boost from using a const
generic function in Rust? In this program we the 2 test()
functions, and the first uses a const
type.
const
generic function. The N bool
is always known at compile-time.bool
parameter. With inlining the compiler may be able to turn the bool
into a constant.const
generic function is much faster, as the compiler can optimize away the constant usage in the function.use std::time::*; fn test<const N: bool>(max: i32) -> i32 { let mut result = 0; for _ in 0..max { for i in 0..max { result += if N == true { 1 } else { 2 }; if max - 2 == i { break; } } } result } fn test_no_generic(n: bool, max: i32) -> i32 { let mut result = 0; for _ in 0..max { for i in 0..max { result += if n == true { 1 } else { 2 }; if max - 2 == i { break; } } } result } fn main() { if let Ok(max) = "100000".parse() { // Ensure bool is not known at compile-time. let is_true = max % 2 == 0; let mut sum1 = 0; let mut sum2 = 0; // Version 1: use const generic function. let t0 = Instant::now(); if is_true { sum1 += test::<true>(max); } else { sum1 += test::<false>(max); } println!("{}", t0.elapsed().as_millis()); // Version 2: use non-generic function. let t1 = Instant::now(); sum2 += test_no_generic(is_true, max); println!("{}", t1.elapsed().as_millis()); println!("{} = {}", sum1, sum2); } }118 ms (const) 782 ms 1409965408 = 1409965408
When we create a generic const
function, we tell the compiler to generate multiple versions of the function. Each version has constants in it based on the parameter.
We can use const
generic functions to generate multiple copies of functions that are better-optimized. With some code changes, this can improve performance by reducing work at runtime.