1

I am a bit confused by Option::map(). The documentation says it accepts a FnOnce.

If so, why do a and b cause compilations errors?

let mut v = 3;

let mut a: &FnOnce(u32) -> u32 = &|x: u32| { v = x; x };
let mut b: &FnMut(u32) -> u32 = &|x: u32| { x };
let mut c: &Fn(u32) -> u32 = &|x: u32| { x };

let o = Option::Some(3);

o.map(a); // trait bound `std::ops::FnOnce(u32) -> u32: std::ops::Fn<(u32,)>` is not satisfied
o.map(b); // trait bound `std::ops::FnMut(u32) -> u32: std::ops::Fn<(u32,)>` is not satisfied
o.map(c); // works

Shouldn't all of them, including a and b, implement FnOnce according to this post?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
left4bread
  • 1,514
  • 2
  • 15
  • 25

1 Answers1

3

The problem is that you are not calling Option::map with a FnOnce directly, but with a &FnOnce.

But if you look at the implementors for FnOnce, you'll notice that while FnOnce is implemented for &Fn, it is not implemented for &FnOnce or &FnMut. To understand why, consider the following:

let a: &FnOnce(u32) -> u32 = &|x: u32| { v = x; x };
let b: &FnOnce(u32) -> u32 = a;

a(42); // would be allowed if `&FnOnce: FnOnce`, moves `a`
       // actually gives: 
       // error[E0507]: cannot move out of borrowed content
a(6);  // would not be allowed as `a` was moved
b(7);  // would be allowed if `&FnOnce: FnOnce`
       // oh no! this calls the function again!
mcarton
  • 27,633
  • 5
  • 85
  • 95