0

While this is a trivial example, I have multiple generic functions which need to work across a few dozen possible values. The actual value is not known until runtime, as it is provided as an argument.

#![feature(generic_const_exprs)]

fn generic_division<const DIVISOR: u64>(denominator: u64) -> u64 {
    denominator / DIVISOR
}

fn main() {
    let denominator = 12;
    for i in 1..5 {
        let dividend = match i {
            1 => generic_division::<1>(denominator),
            2 => generic_division::<2>(denominator),
            3 => generic_division::<3>(denominator),
            4 => generic_division::<4>(denominator),
            _ => panic!()
        };
        println!("{} / {} == {}", denominator, i, dividend);
    }
}

Is there a better way than above? All other approaches have run into const issues.

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
fadedbee
  • 42,671
  • 44
  • 178
  • 308
  • 3
    Use a parameter instead of a const generic parameter? – Chayim Friedman Jan 25 '23 at 08:34
  • 3
    "The actual value is not known until runtime" – that's a clue; const generics are for compile-time constants. – Sven Marnach Jan 25 '23 at 09:30
  • @SvenMarnach I should have written "One of a small set of possible values is not selected until runtime". I expect the compiler to create multiple function instances, one specialised for each choice of a small number of possible values (for `DIVIDEND` in this toy example). – fadedbee Jan 25 '23 at 20:10
  • @fadedbee For many cases, that's slower than simply using a runtime parameter, due to less efficient use of code caches and additional branching when deciding what function to call. Make sure you have benchmarks showing clearly that the additional complexity pays off. – Sven Marnach Jan 26 '23 at 16:06

1 Answers1

2

When you use const generic parameter, intent is to guarantee that parameter is known at compile time. Its not even syntactically possible to pass runtime value here. If you want to ensure your function can be constant folded and also accept runtime values, if needed, you can use const fn. Lets give an example.

// prefixing function with const
const fn const_divisor(value: u64, divisor: u64) -> u64 {
    value / divisor
}

// no difference compared to non const fn
fn main() {
    let denominator = 12;
    for i in 1..5 {
        let dividend = const_divisor(denominator, i);
        println!("{} / {} == {}", denominator, i, dividend);
    }
}

// the advantage of const fn is that you can use it in constants
const REAL_USECASE: u64 = const_divisor(1024, 46);
Jakub Dóka
  • 2,477
  • 1
  • 7
  • 12
  • `const fn` is not related to constant folding. LLVM can do constant folding on non-`const` functions too. In fact, the information whether the function was `const` or not is completely erased at the LLVM IR level. The only difference is that `const fn` can be called in constants and statics. – Chayim Friedman Jan 25 '23 at 12:50
  • Should I then write "constatnt folded by the compiler" – Jakub Dóka Jan 25 '23 at 13:47