Crate namable_closures

Source
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§

Modules§

Macros§