genericsrustmonomorphism-restriction

Why does rust only allow standalone constant for array size?


I want to use a function that concatenates two arrays with a declaration like so:

fn concatenate<const COUNT1: usize, const COUNT2: usize>(a: [i32;COUNT1], b: [i32;COUNT2]) -> [i32;{COUNT1+COUNT2}];

The problem is the return type. Here is the specific error:

error: generic parameters may not be used in const operations
 --> src\main.rs:4:101
  |
4 | fn concatenate<const COUNT1: usize, const COUNT2: usize>(a: [i32;COUNT1], b: [i32;COUNT2]) -> [i32;{COUNT1+COUNT2}] {
  |                                                                                                     ^^^^^^ cannot perform const operation using `COUNT1`
  |
  = help: const parameters may only be used as standalone arguments, i.e. `COUNT1`

This function seems very easy to monomorphize and I don't understand why the compiler doesn't allow it. The rust book only states(twice) it is not allowed, but doesn't explain why:

Const parameters can be used anywhere a const item can be used, with the exception that when used in a type or array repeat expression, it must be standalone (as described below).

As a further restriction, const parameters may only appear as a standalone argument inside of a type or array repeat expression.

Does anyone know how this pattern is against the rust model, because at least from my point of view it definitely isn't an implementation limitation. Here's the whole function if it will help:

fn concatenate<const COUNT1: usize, const COUNT2: usize>(a: [i32;COUNT1], b: [i32;COUNT2]) -> [i32;{COUNT1+COUNT2}] {
    let mut output = [0i32;{COUNT1+COUNT2}];
    output.copy_from_slice(
        &a.iter().chain(b.iter()).map(|&item| item).collect::<Vec<i32>>()
    );
    output
}

Solution

  • You need to enable the feature generic_const_exprs, which is only available in nightly for the moment (rust 1.63):

    #![feature(generic_const_exprs)]
    
    fn concatenate<const COUNT1: usize, const COUNT2: usize>(a: [i32;COUNT1], b: [i32;COUNT2]) -> [i32;{COUNT1+COUNT2}] {
        let mut output = [0i32;{COUNT1+COUNT2}];
        output.copy_from_slice(
            &a.iter().chain(b.iter()).map(|&item| item).collect::<Vec<i32>>()
        );
        output
    }
    

    Playground