Understanding const fn and slices

I naively thought const fn functions were completely evaluated at compile time. However this produces a runtime panic:

const fn foo(a: &[u8]) -> u8 {
    a[4]
}

fn main() {
    foo(&[0,1,2,3]); // array too small
}

Playground link.

This was surprising to me. Surely either slice indexing shouldn’t be allowed or should be compile time evaluated? Or am I misunderstanding the const fn concept?

const fn is a function that can be evaluated at both compile time and runtime. To restrict it to run purely at compile time, assign its return value to constant like const FOO: u8 = foo(..);.

4 Likes

To be a litle more specific, constant functions will only be evaluated at compile time when they are used within a constant context.

Here is an example that does fail at compile time:

const fn foo(a: &[u8]) -> u8 {
    a[4]
}

const FOO: u8 = foo(&[0, 1, 2, 3]);

fn main() {
    println!("{}", FOO);
}
error: any use of this value will cause an error
 --> src/main.rs:2:5
  |
2 |     a[4]
  |     ^^^^
  |     |
  |     index out of bounds: the len is 4 but the index is 4
  |     inside call to `foo` at src/main.rs:5:17
...
5 | const FOO: u8 = foo(&[0, 1, 2, 3]);
  | -----------------------------------
  |
  = note: #[deny(const_err)] on by default
3 Likes

So const constructs can only compile in a constant context? :stuck_out_tongue:

EDIT: this is actually a harder toungue twister than it seems… try 5 times fast!

4 Likes

Ah thanks to you both. I didn’t realize that it pulled double duty but thinking about it that does make sense.

I think I was missing the nuance of “const fn can be evaluated at compile time”.

2 Likes

You can use a macro to force constant evaluation:

const fn foo(a: &'_ [u8]) -> u8
{
    a[4]
}

macro_rules! const_call {(
    $expr:expr => $T:ty
) => ({
    #[deny(const_err)]
    const RET: $T = $expr;
    RET
})}

fn main ()
{
    const_call!(
        foo(&[0,1,2,3]) => u8 // array too small
    );
}
error: any use of this value will cause an error
 --> src/main.rs:3:5
  |
3 |     a[4]
  |     ^^^^
  |     |
  |     index out of bounds: the len is 4 but the index is 4
  |     inside call to `foo` at src/main.rs:16:9
...
16 | /     const_call!(
17 | |         foo(&[0,1,2,3]) => u8 // array too small
18 | |     );
   | |______- in this macro invocation
5 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.