Expand description
This crate supports nameable closures, without requiring any language changes.
§Why do we need this
The Rust language supports closures, however its concret types are
anonymous and developers cannot expect same size, and so not able to
put similar closures in containers that requires Sized
constraint.
However, there is an exception: if a closure does not refer to its
captured variables, it can be coerce to a fn
type, which is a pointer
type and it is Sized
.
This library extends this idea by requesting an additional State
field,
which is just the tuple of the captured variables. So if two closure have
the same signiture for function calls and have the same state type, they
are considered the same type.
§How to use
There are 5 structures being defined, and they are correspond to different use cases.
§Closure
This struct works for closures that only intended to refer to the state field, not modifying them nor owns them. Furthermore, it does not own its state, so dropping this struct will not drop its state.
§ClosureRef
This structure works like the above, but it owns its state. So dropping it will drop its state.
§ClosureMut
This struct works for closures that will mutate its state, but would not drop its state when called. So it can be called multiple times, but will have different effects on each call. Because it does not own its state, the state will not be dropped when the struct was dropped.
Amount all 5 variants, this is the only struct that does not support Copy
and Clone
at all, because it is not possible to copy or clone a mutable reference.
§ClosureRefMut
This struct works like the above, but it owns its state. Because it owns its state, so it
can be Copy
or Clone
if its state is Copy
or Clone
, without problems.
§ClosureOnce
This struct owns its state, and will drop its state when called.
§The closure!
macro
To create closures, use the closure!
macro.
The format of the macro is:
closure!([ref]? [mut]? state_variable=expression => [closure definition])
The closure definition is like the usual (including the move
keyword), but type
annotations are not supported. However, we are providing a type that is namable,
so you can always specify the result type to constraint the variables involved, so
this should not be a big due.
If the macro sees a ref
keyword in front of the closure, it will expect a move
keyword before the closure body. If ref
is specified, the state variable is a reference
to its value.
If move
is not specified, the state expression must be typed as a reference and match the
mutation specification of the state variable. The closure body can only access the state
variable and the variables in the closure definition header.
Macro Grammar | Struct |
---|---|
closure!(state=exp => |x,y| body(x,y,state)) |
Closure |
closure!(state=exp => move |x,y| body(x,y,state)) |
ClosureOnce |
closure!(mut state=exp => |x,y| body(x,y,state)) |
ClosureMut |
closure!(mut state=exp => move |x,y| body(x,y,state)) |
ClosureOnce (with mutable state ) |
closure!(ref state=exp => move |x,y| body(x,y,state)) |
ClosureRef |
closure!(ref mut state=exp => move |x,y| body(x,y,state)) |
ClosureRefMut |
Examples:
// state refered as reference in body, but moved to the closure
let add_ten:ClosureRef<i32,(i32,),i32>
= closure!(ref state=10 => move |i| i+*state);
assert_eq!(add_ten.stable_call((1,)),11);
let offset:ClosureRef<Point,(i32,i32),Point>
= closure!(ref state=Point::new(10,20) => move |a,b| Point::new(state.x+a,state.y+b));
let p = offset.stable_call((1,2));
assert_eq!(p.x,11);
assert_eq!(p.y,22);
// state refered as reference in body, and not moving
let state = 10;
let add_ten:Closure<i32,(i32,),i32>
= closure!(state=&state => |i| i+10);
assert_eq!(add_ten.stable_call((1,)),11);
let state = Point::new(10,20);
let offset:Closure<Point,(i32,i32),Point>
= closure!(state=&state => |a,b| Point::new(state.x+a,state.y+b));
let p = offset.stable_call((1i32,2i32));
assert_eq!(p.x,11);
assert_eq!(p.y,22);
// state refered as mutable reference in body, but moved to closure
let mut accumulate:ClosureRefMut<i32,(i32,),i32>
= closure!(ref mut state=0 => move |c| {*state+=c;*state});
assert_eq!(accumulate.stable_call_mut((1,)),1);
assert_eq!(accumulate.stable_call_mut((2,)),3);
// state refered as mutable reference in body, but not moving
let mut state = 0;
{
let mut match_cnt:ClosureMut<i32,(i32,i32),()>
= closure!(mut state=&mut state => |a,b| if a==b { *state+=1 });
for i in 0..10 { match_cnt.stable_call_mut((i,i*3%10)); }
}
assert_eq!(state,2);
// state moved to body and so to the closure
let sign_on:ClosureOnce<Passwd,(String,),Result<(),io::Error>>
= closure!(passwd=Passwd::get_from_cache() => move |user| authenticate(user,passwd));
sign_on.stable_call_once(("123".to_string(),));
let auth:ClosureOnce<RoleSet,(String,Passwd),Result<(),io::Error>>
= closure!(role_set=RoleSet::from_config() => move |user,passwd| check_user(role_set,user,passwd));
let send_data:ClosureOnce<MyStream,(&[u8],),Result<usize,io::Error>>
= closure!(mut stream=MyStream::new() => move |data| stream.write_all(data));
let read_data:ClosureOnce<MyStream,(&mut [u8],usize),Result<(),io::Error>>
= closure!(mut stream=MyStream::new() => move |buf,len| stream.read_exact_ex(buf, len));
Re-exports§
pub use closures::Closure;
pub use closures::ClosureMut;
pub use closures::ClosureOnce;
pub use closures::ClosureRef;
pub use closures::ClosureRefMut;
pub use stable_fn::StableFn;
pub use stable_fn::StableFnMut;
pub use stable_fn::StableFnOnce;
pub use closure_rec::ClosureOnceRec;
pub use closure_rec::ClosureRecMut;
pub use closure_rec::ClosureMutRec;
pub use closure_rec::ClosureRec;
Modules§
Macros§
- The macro to create closures.