The simplest way to show concurrency is through parallelism, so you'll mainly see three kind of closures:
-
FnOnce(), for "the callee will need to call the callback at most once" kind of situations, which allows the closure to be optimized by consuming its captured environment when called. Std lib examples:-
And, with an added
Send(+ 'staticfor the non-scoped API) bound:
thread::spawn()
-
FnMut()for something that is callable multiple times sequentially / non-concurrently.
Stdlib examples:-
the whole plethora of
Iteratoradaptors / consumers -
Sometimes with
Sendand/or'staticbounds for things called from another thread or abitrarily late respectively.
-
-
Fn() + Sync, for something callable in parallel (Fn()-> callable concurrently / through multiple / shared handles;Sync-> these handles can cross thread boundaries. Hence why it can be called from multiple threads at once). In practice, there are also+ Send + 'staticbounds, since the moment multiple threads are involved, the "end of life" flows can rarely be guaranteed at compile-time, which thus requires'staticbounds and dynamic / multiple ownership, e.g.,Arcs.- Comparatively to the previous batch of stdlib single-threaded / sequential iterator adaptors which take
FnMuts,::rayon's own set of iterator adaptors / consumers which feature thatFn… + Sync (+ Send)bounds.
- Comparatively to the previous batch of stdlib single-threaded / sequential iterator adaptors which take
But for the sake of the example, you could, quite rarely, see a non-Sync Fn requirement, such as some thread-local hook: since the hook could technically be triggered when the very hook is being run (re-entrancy, another form of concurrency which happens not to require multi-threading to happen), then such a hook would need to be Fn().
If by sharing you mean sharing-and-using, then yes, Rust will prevent it, and that's the whole point about Rust, although not limited to Fn…s: if you are sharing something, you only get & access to it, and thus only have access to the &shared-compatible parts of that element's API, such as the &self-based methods. Calling a Fn is such as method.
If you are not sharing, then you can get a &unique access to it (dubbed &mut in Rust), and thus can use the stronger but more restrictive parts of the item's API: the &mut-based functions, such as &mut self methods, which includes calling a FnMut.
Indeed, and that's a code smell since it's "as useful as nipples on a breastplate": Sync allows &shared access to cross threads, but FnMut requires &unique access to be callable, so while you may be sending &shared-access-yielding handles across threads, you'll never be calling that FnMut().