When thinking about whether to use Fn or FnMut for this bound on F, I wondered if every FnMut can be converted to an Fn where needed (assuming we're single-threaded).
Consider these two conversion functions which convert between Fn and FnMut:
pub fn fn_to_fn_mut<F, A, B>(f: F) -> impl FnMut(A) -> B
where
F: Fn(A) -> B,
{
// works because `FnMut` is a supertrait of `Fn`
f
}
pub fn fn_mut_to_fn<F, A, B>(f: F) -> impl Fn(A) -> B
where
F: FnMut(A) -> B,
{
let f = std::cell::RefCell::new(f);
move |a| (f.borrow_mut())(a) // how to trigger this to panic?
}
Example use:
fn twice<F, A, B>(f: F, arg: A) -> B
where
F: Fn(A) -> B,
A: Clone,
{
f(arg.clone());
f(arg)
}
fn main() {
let mut counter: i32 = 0;
let f = |()| counter += 1;
twice(fn_mut_to_fn(f), ());
assert_eq!(counter, 2);
}
It's not directly pointed at you because your code is sound, but every time I hear people say "assuming we're single-threaded" to justify shared mutation it reminds me of this blog post:
You are right in that the only way to make this code panic is if we do f.borrow_mut() while f is already being called. For a "trivial" function it's going to be pretty hard to pull this off, but I could see it happening in really complex scenarios, expecially when Arc<dyn Trait> might be involved and the closure is (indirectly) passed into itself.
I'm not sure if it counts because the fault isn't with fn_mut_to_fn(), but this will panic:
use std::cell::RefCell;
pub fn fn_mut_to_fn<F, A, B>(f: F) -> impl Fn(A) -> B
where
F: FnMut(A) -> B,
{
let f = RefCell::new(f);
move |a| (f.borrow_mut())(a)
}
fn main() {
let f = RefCell::new(|x: i32| println!("{}", x));
let wrapped_fn = fn_mut_to_fn(|x| (f.borrow_mut())(x));
let _borrow = f.borrow();
wrapped_fn(1);
}
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:3:11
|
3 | x(x)
| - ^ cyclic type of infinite size
| |
| arguments to this function are incorrect
|
help: use parentheses to call this trait object
|
3 | x(x(/* value */))
| +++++++++++++
error[E0644]: closure/generator type that references itself
--> src/main.rs:2:14
|
2 | let f = &|x: &dyn Fn(_)| {
| ^^^^^^^^^^^^^^^ cyclic type of infinite size
|
= note: closures cannot capture themselves or take themselves as argument;
this error may be the result of a recent compiler bug-fix,
see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
for more information
= note: required for the cast from `[closure@src/main.rs:2:14: 2:29]` to the object type `dyn Fn(_)`
Some errors have detailed explanations: E0308, E0644.
For more information about an error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 2 previous errors
Well the Rc gymnastics is only needed to avoid lifetime errors; the rest is just the old straightforward "initialize to something irrelevant then replace when self-reference is already in place" trick.