nclosure
nclosure is a library designed to ease the use of closures and generic, composable computation in APIs where anonymous trait objects, like impl FnOnce cannot currently be placed.
In particular, it provides an ergonomic method of defining closures such that the state embedded within the closure can be separated from the raw fn pointer, which can be encoded in the type system with a named type.
Furthermore, it provides versions of the FnOnce, FnMut and Fn traits that can be implemented in a custom manner upon structures. This allows for the encoding of composed computation into nameable types, of which this crate provides some, hence allowing for it's use as a return type in areas of code where anonymous types are not yet allowed. https://github.com/rust-lang/rust/issues/29625
Closure types
The primary function of this crate is to enable using traits with functions that provide computations as output or that may require specific, nameable representations of computation as input for storage.
In this crate, a closure refers to a bundle of some specific state, along with a separate function pointer. Standard rust closures are also essentially this, but each closure is an entirely new, unnameable type in Rust, and hence difficult to put into uniform storage without allocation/boxing/dynamic dispatch/etc. The closures in the nclosure crate are uniform types such that you can store different function pointers or state values in one closure type.
[ClosureOnce]
This type is a closure that can only be called once, and it consumes it's inner state. Remember, though - the inner state of the closure is able to hold things like references just fine.
It is what you almost always want for any one-shot computation.
[ClosureMut]
This is a closure that can be called repeatedly to generate values, mutating it's inner state - this means that calling it requires exclusive references to the closure. If you just need to hold a reference for a single computation, [ClosureOnce] will do just fine.
[Closure]
This holds a bare computation that can be called repeatedly without holding an exclusive reference to it's internal state, including stuff like cross threads. This is the most general form of closure - it could own it's state or it could hold some other reference.
Traits
As a consequence of the current inability to define your own [Fn] , [FnMut] , and [FnOnce] implementations on custom types - like the various closure types defined here - this crate also defines it's own custom computation types that are a superset of the built-in types as a workaround (in particular, [Co] , [CoMut] and [CoOnce] , which are automatically implemented for the Rust core traits).
These can be freely converted to the Rust core impl-trait types using [as_fn_once] , [as_fn_mut] , and [as_fn] .
Examples
Section containing example usages of this crate.
The benefit of using this crate is that the resulting computation type is nameable solely in terms of inner state, arguments, and return values - as opposed to being an opaque impl FnMut type.
This enables several things. Not only does it make it easier to define storage for closures, it also allows easier usage of automatically derived (or otherwise) traits - for instance, with respect to thread synchronisation and Co/CoMut/CoOnce.
In particular, the compositional methods provided by this trait preserve the repeatability (Co vs CoMut vs CoOnce) of their generic parameters, which means that a single method can preserve the repeatability of arbitrary composition of functions because the resulting composition is itself a named type rather than an anonymous impl Fn/impl FnMut/impl FnOnce type.
Closure Constructions
Some examples of how to ergonomically construct nameable closures using this crate.
Counter
The first example is the construction of a closure that generates increasing values, illustrating the simple means of constructing a closure state.
use *;
/// Count from the given number upwards.
// Note that unfortunately we have to provide an empty tuple argument, because the computation traits have their arguments as a tuple.
let mut counter = count_from.as_fn_mut;
let first = counter;
let second = counter;
let third = counter;
assert_eq!
Fibonacci
This demonstrates the construction of a more complex closure state using the fluent API, to produce the fibonacci sequence.
use *;
/// Make a function that generates the Fibonacci sequence, starting from 0
let mut fibgen = fibonacci.as_fn_mut;
let vals = ;
assert_eq!;
Closure Composition
One of the useful aspects of this crate is the clean composition of computation and state, in a manner that produces nameable types. This section illustrates methods for doing that.
Chaining Closures (Squared Counter)
This illustrates an example of chaining closures together in a nameable fashion.
use *;
use Mul;
/// Square the output of a function
/// Note here that the output type is actually *named*, which enables
/// some very useful properties. In particular, if the input computation
/// implements `Co` or `CoMut` instead of just `CoOnce`, then the
/// composed computation will also implement those traits (at least as
/// long as the chained computations also implement them).
// Note how when the counter is `CoMut`, the resulting function is also.
let mut squared_ctr = square_result.as_fn_mut;
let vals = ;
assert_eq!