1

Since Rust array length is known at compile time, how do I get that length as a constant integer? It is possible to get the non-const len() of the slice corresponding to the whole array. It doesn't seem like rust has begin and end iterators like in c++, and it's not clear to me if I can get the byte length and divide it by the byte size of elements. I'm still a noob at Rust, so it's possible I'm missing something but I've been looking for the past few days for a way to do this. I'm aware that cloning an array is possible, but that's only useful if I want a clone, and iterating over every element of the array is slow compared to just getting the length from the type of the array. How can I do this? The Rust documentation says that the type signature of arrays is [ele_type; ele_number], so the compiler does keep track of the length of arrays after creation. I'm aware that std::any::type_name::() can be used to print the type of a variable, but its output isn't const.

I am aware of a related question already posed, but my question isn't answered by existing answers and the question is old (from 2017) so things may have changed and it would not likely be suggested to people.

Edit 1 to add example:

fn main()
{
    let test_arr = [0.f64; 10];
    do_something(test_arr);

}

fn do_something<const C: usize>(array: [f64; C])
{
    // This causes an error because len() returns a nonconst uint,
    // and so can’t be used to create new arrays
    // C can also not be used because it counts as external
    let triple_size_array = [f64; array.len()*3];
}

Edit 2: To clarify, it is possible to do array = [f64; C]; directly, but there's other things that can't be done. As shown below, I can't do any integer math with C to define an array of a different size, I can only use the generic parameter to define an array directly.

13 | fn ftcs_update<const C: usize>(u: &[f64;C], f: fn(f64)->f64, l: f64)
   |                      - const parameter from outer function
14 | {
15 |     const grid_size: usize = C;
   |                              ^ use of generic parameter from outer function

error: aborting due to previous error

For more information about this error, try `rustc --explain E0401`.
error: generic parameters may not be used in const operations
  --> main.rs:16:22
   |
16 |     let u_new = [0.; C*3];
   |                      ^ cannot perform const operation using `C`
   |
   = help: const parameters may only be used as standalone arguments, i.e. `C`

Edit 3: In response to Kevin's suggestion, it doesn't seem this solution works either.

error[E0401]: can't use generic parameters from outer function
  --> main.rs:15:30
   |
13 | fn ftcs_update<const C: usize>(u: &[f64;C], f: fn(f64)->f64, l: f64)
   |                      - const parameter from outer function
14 | {
15 |     const grid_size: usize = C;
   |                              ^ use of generic parameter from outer function

error: aborting due to previous error

For more information about this error, try `rustc --explain E0401`.
Liam Clink
  • 189
  • 1
  • 11
  • 2
    Can you provide an example of some code where you would like to know this? I can't think of a case where you _know_ you have an array but wouldn't know what size it is, since the length is part of the type. – cdhowie Jul 11 '22 at 23:57
  • The issue isn’t that *I* don’t know the size, it’s that I can’t access it later. Since the size is part of the type definition, it should be trivial to access the number of elements as a const uint – Liam Clink Jul 12 '22 at 00:14
  • 2
    In your example code, why can you not just use `C` instead of `array.len()`? – cdhowie Jul 12 '22 at 00:26
  • @chdhowie Ok I checked and that is possible if you only want to make an array with length ```C```, but creating an array using ```C``` that isn't ```C``` long, such as making an array that is shorter or longer or a multiple in length doesn't appear possible. – Liam Clink Jul 12 '22 at 00:58

2 Answers2

5

Const generics are an area of Rust that is still very much in development and in places it is just not as far as constexpr and the like are in C++. This is one of them.

There is an implementation of const generic expressions (i.e. expressions that can be evaluated at compile time, that rely on generic parameters), but this is still incomplete, unstable, and available only in nightly versions of Rust. See the tracking issue https://github.com/rust-lang/rust/issues/76560 for more details.

Here is your example patched up to work with what is currently implemented:

#![feature(generic_const_exprs)]
// We still need this feature and the nightly compiler, because this is still
// in the works and the implementation is incomplete!

fn main() {
    // I changed your array initializers, because you were using the type
    // where a value should have been.
    // The way you wrote them is how you write the type signature for these arrays.
    // I've added the type signature here, but that wasn't necessarily
    // and merely for illustrative purposes. The compiler can infer it here.
    let test_arr: [f64; 10] = [0_f64; 10];
    do_something(test_arr);
}

// The where is still required to constrain this function to values of C where
// C * 3 is a valid size for an array. This might change in the future.
fn do_something<const C: usize>(_array: [f64; C])
where
    [f64; C * 3]: Sized,
{
    // Here you can now just use the generic parameter in an expression.
    // In other places you may have to surround such an expression by curly braces
    // for the sake of syntactical unambiguity.
    let triple_size_array = [0_f64; C * 3];
    println!("{}", triple_size_array.len());
}

And here is a link to a Rust playground with this code.

Currently, if you leave out the where clause, you'll get the following error which tells you what part of the code requires the constraint and what constraint to add:

error: unconstrained generic constant
  --> src/main.rs:22:37
   |
22 |     let triple_size_array = [0_f64; C * 3];
   |                                     ^^^^^
   |
   = help: try adding a `where` bound using this expression: `where [(); C * 3]:`

Note that the element type suggested for the where clause is not f64, but rather the unit type / the 0-tuple (). This is because for the constraint only the length of the array being a valid length is relevant, not the type of the elements.

hcsch
  • 66
  • 1
  • 3
  • Oh right, well I fixed the array initializer. I do believe float literals are required to have a . in them, maybe this isn't the case if _ is used. Thank you for adding the issue so I can follow this and maybe help. So to clarify, this will not compile if the where statement isn't included? Looks like using the nightly Rust compiler and the where is the only difference here – Liam Clink Jul 12 '22 at 02:03
  • Do not use `generic_const_exprs` in production. – Chayim Friedman Jul 12 '22 at 05:43
  • @ChayimFriedman Don't ever or not as long as it is unstable? – Liam Clink Jul 12 '22 at 16:56
  • 1
    @LiamClink As long is it is buggy, perhaps unsound and can crash the compiler. In other words, as long as it is marked incomplete. – Chayim Friedman Jul 13 '22 at 02:04
  • 1
    @LiamClink I've added some more info on the where clause to the answer. In addition to using nightly Rust and adding a where clause, you also need to enable the crate level feature `generic_const_exprs`. As the @ChayimFriedman says above, you should stay away from that feature (or other unstable and incomplete features like it) for production code for as long as it is unstable. Feel free to experiment with it in non-production code though. If this interests you, you might also want to subscribe to the tracking issue on GitHub – hcsch Jul 13 '22 at 08:10
-1

It seems that an alternative way to address the problem is to extract the generic const into a local const, i.e.

fn foo<const C: usize>()
{
    const A: usize = C;
    // Do something with A
}

EDIT: I am wrong, this does not work.