rustctfe

Why can't the compiler detect that functions are constant without annotating them with const?


In Rust, const functions are quite limited in what code can be put inside them, e.g. for loops aren't allowed, nor are any non-const function calls. I understand that there are issues with heap allocations in const functions, but why is it that for example the below code isn't valid:

fn add(a: u8, b: u8) -> u8 {
    a + b
}
const A: u8 = add(1, 2);

This would be valid if add was declared as a const function.


Solution

  • Why are const functions necessary at all?

    Constant functions are declared const to tell the compiler that this function is allowed to be called from a const context. Const-eval is done in a tool called miri. Miri is not capable of evaluating all expressions, and so const functions are pretty limited. There is a tracking issue of extensions that have been accepted and there is active work going on to make them const-compatible.

    Why can't the Rust compiler detect that this is valid?

    While technically possible, the Rust team decided against it. The primary reason being semantic versioning issues. Changing the body of a function, without changing the signature, could make that function no longer const-compatible and break uses in a const-context. const fn is a contract, and breaking that contract is an explicit breaking change.

    Why aren't even for loops allowed inside them (even though while loops are)?

    As to why for loops aren't allowed, they technically are. The only problem is that the Iterator::next method is not const, meaning that not some iterators may perform operations that are not const-evaluatable. Until a language construct that allows trait methods to be marked as const, or the ability to put const bounds, you will have to stick to regular loops.

    const fn looop(n: usize) {
        let mut i = 0;
        loop {
            if i == n {
                return;
            }
            // ...
            i += 1;
        }
    }