Should you use const generic arguments where not needed?

const generic arguments are quite useful for functions that, for example, need the N declared on a [T; N]. However for functionality that need not consume a const, should const arguments be used? For code where the end use is expected to pass an integer that will never change for example, should a const argument be used? Take for example the following:

fn calculate<const X: usize, const Y: usize, const Z: usize>(other_arg: usize) -> SomeResult {
    // ...
}

The above function is expected to pass around X, Y and Z to various other functions as a const generic arg, all of which perform functionality that would compile fine regardless of whether they were a constant. So my question is, would the above benefit over the following:

fn calculate(x: usize, y: usize, z: usize, other_arg: usize) -> SomeResult {
    // ...
}

called with calculate(1, 2, 3, my_non_const_arg); (Note the const defined 1, 2, 3)?

1 Like

If you use const parameters, the compiler will perform much more work by compiling the function separately for each combination of X, Y, and Z it is used with. This could in principle be valuable for optimization in cases where inlining by itself doesn’t produce a good result, but outside of such an unusual case, it will be entirely wasted effort.

2 Likes

I think "effort" might be a key word here. I got the impression from another post a while back that it isn't really worth the effort (and limitations) to use const generics unless you have to (eg. require the variable to be const). Would anyone agree that this would be an appropriate stance to take on const generic usage?

From the viewpoint of optimization, a const is obviously better than a variable that does not change its value; at the same time, as noted before, the compiler will have to generate different code for each const value.

A less evident (but consequential) effect, however, is that as soon as you have a const parameter the type of your struct changes with the const. For example, if you serialize an instance with a certain const value, it must be deserialized with the same values. This is extremely rigid and restrictive, as it makes it impossible for a user to deserialize a structure without knowing a priori of the value of the const.