3

Is it possible to write a const function that folds over an iterator? When I try:

const fn foo(s: &str) -> u64 {
    return s.chars().fold(0, |accumulator, char| -> u64 {
        return accumulator ^ (char as u64);
    });
}

I get a compiler error:

error: function pointers in const fn are unstable
 --> src/lib.rs:2:30
  |
2 |       return s.chars().fold(0, |accumulator, char| -> u64 {
  |  ______________________________^
3 | |         return accumulator ^ (char as u64);
4 | |     });
  | |_____^

I presume that my anonymous function |x, y| -> x { ... } is passed as a function pointer to fold() and that's what's causing the error.

Is there some kind of const lambda that I can pass to fold here, or can I just use a for loop instead and accumulate the result in a mutable variable which I then return from the foo function? I have absolutely no Rust experience...

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lassi
  • 3,522
  • 3
  • 22
  • 34

2 Answers2

5

No, you cannot do this in stable Rust 1.59. You will need to make your function non-const:

fn foo(s: &str) -> u64 {
    s.chars().fold(0, |accumulator, char| {
        accumulator ^ (char as u64)
    })
}

Note I removed the explicit return keywords and closure return type to be idiomatic.

See also:


If you attempt this in nightly Rust:

const fn foo(s: &str) -> u64 {
    s.chars().fold(0, |accumulator, char| {
        accumulator ^ (char as u64)
    })
}

You'll get a different error:

error[E0015]: cannot call non-const fn `core::str::<impl str>::chars` in constant functions
 --> src/lib.rs:2:7
  |
2 |     s.chars().fold(0, |accumulator, char| {
  |       ^^^^^^^
  |
  = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

error[E0015]: cannot call non-const fn `<Chars as Iterator>::fold::<u64, [closure@src/lib.rs:2:23: 4:6]>` in constant functions
 --> src/lib.rs:2:15
  |
2 |       s.chars().fold(0, |accumulator, char| {
  |  _______________^
3 | |         accumulator ^ (char as u64)
4 | |     })
  | |______^
  |
  = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants

Calling Iterator::fold requires several extensions to the original const fn RFC 911 to be implemented. For example, it's expressly forbidden by the original RFC:

Traits, trait implementations and their methods cannot be const

Since closures are implemented as generics backed by traits, I don't immediately see that they could be easily implemented for the same reason.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thanks for the thorough edits. Do you know if there's a proposed solution to let us use higher-order functions inside const functions in the future? I looked at the RFC thread linked from the last question you linked but I didn't understand it. – Lassi Apr 02 '19 at 16:25
  • Would be nice if `no` wasn't still the answer. – Squirrel Apr 05 '22 at 17:10
  • @Squirrel wishing won't change it ‍♀️. You can attempt to help implement the required changes to the Rust compiler to help get it closer. – Shepmaster Apr 05 '22 at 17:15
1

For somebody who struggled with this – for now you can still use the plain loop or while in const fn expressions.

Dmitriy Kovalenko
  • 3,437
  • 3
  • 19
  • 39