const Generic Example
This page was last reviewed on May 11, 2023.
Dot Net Perls
Const generic. 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.
const fn
size of
Array example. 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]
Bool example. 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.
Info The test() function can have a true or false constant type. We specify this type when we call test() in main.
Tip In this program, the 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); }
Performance, 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.
Version 1 This version of the code uses a const generic function. The N bool is always known at compile-time.
Version 2 Here we just use a bool parameter. With inlining the compiler may be able to turn the bool into a constant.
Result Using the 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
Const performance notes. 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.
So The compiler can generate multiple versions of the code, and optimize each one based on its constants.
And No runtime cost is paid for these compile-time features. This is a true zero-cost abstraction.
A summary. 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.
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on May 11, 2023 (edit link).
© 2007-2024 Sam Allen.