#[macro_export]
macro_rules! closure {
(@inner move $var:ident $($tail:tt)*) => {
closure!(@inner $($tail)*)
};
(@inner ref mut $var:ident $($tail:tt)*) => {
let $var = &mut $var;
closure!(@inner $($tail)*)
};
(@inner ref $var:ident $($tail:tt)*) => {
let $var = &$var;
closure!(@inner $($tail)*)
};
(@inner clone $var:ident $($tail:tt)*) => {
let $var = $var.clone();
closure!(@inner $($tail)*)
};
(@inner , $($tail:tt)*) => {
closure!(@inner $($tail)*)
};
(@inner move $($closure:tt)*) => {
compile_error!("keyword `move` not permitted here.");
};
(@inner $($closure:tt)*) => {
__assert_closure!($($closure)*);
move $($closure)*
};
($($args:tt)*) => {{
closure!{@inner $($args)*}
}};
}
#[macro_export]
macro_rules! __assert_closure {
(move $($any:tt)*) => {};
(| $($any:tt)*) => {};
(|| $($any:tt)*) => {};
($($any:tt)*) => {
compile_error!(concat!(
"the supplied argument is not a closure: `", stringify!($($any)*), "`")
);
};
}
#[cfg(test)]
mod test {
struct Foo {
bar : usize
}
impl Foo {
fn new(bar: usize) -> Self {
Foo { bar }
}
fn bar(&self) -> usize {
self.bar
}
}
#[test]
fn no_capture_one_line_1() {
let closure = closure!(|| true);
assert_eq!(true, closure());
}
#[test]
fn no_capture_one_line_2() {
let closure = closure!(|| assert!(true));
closure();
}
#[test]
fn no_capture_one_line_3() {
let closure = closure!(|| 5 * 5);
assert_eq!(25, closure());
}
#[test]
fn no_capture_with_arg() {
let closure = closure!(|x| x * x);
assert_eq!(25, closure(5));
}
#[test]
fn no_capture_with_arg_type_hint_1() {
let closure = closure!(|x: usize| x * x);
assert_eq!(25, closure(5));
}
#[test]
fn no_capture_with_arg_type_hint_2() {
let closure = closure!(|x: usize| {
x * x
});
assert_eq!(25, closure(5));
}
#[test]
fn no_capture_with_arg_and_return_type() {
let closure = closure!(|x: usize| -> usize {
x * x
});
assert_eq!(25, closure(5));
}
#[test]
fn no_capture_with_mut_arg() {
let closure = closure!(|mut string: String, n: usize| -> usize {
for _ in 0..n {
string.push('x');
}
string.len()
});
let string = String::from("xxxx");
assert_eq!(10, closure(string, 6));
}
#[test]
fn no_capture_w_return_type() {
let closure = closure!(|| -> &str {
"result"
});
assert_eq!("result", closure());
}
#[test]
fn single_capture_move() {
let string = String::from("move");
let closure = closure!(move string || string.len());
assert_eq!(4, closure());
}
#[test]
fn single_capture_move_mut() {
let mut string = String::from("move");
let closure = closure!(move string || {
string.clear();
string.push_str("moved");
string
});
assert_eq!("moved", &closure());
}
#[test]
fn single_capture_ref() {
let foo = Foo::new(50);
let closure = closure!(ref foo || {
let bar = foo.bar();
assert_eq!(50, bar);
});
closure();
}
#[test]
fn single_capture_mut_ref() {
let mut foo = Foo::new(100);
{
let mut closure = closure!(ref mut foo || {
foo.bar += 10;
});
closure();
}
assert_eq!(110, foo.bar);
}
#[test]
fn single_capture_clone() {
use std::rc::Rc;
let rc = Rc::new(50);
let closure = closure!(clone rc || -> usize {
Rc::strong_count(&rc)
});
assert_eq!(2, closure());
}
#[test]
fn single_capture_with_arg() {
let mut foo = Foo::new(10);
let mut closure = closure!(ref mut foo |x: usize| {
foo.bar += x;
foo.bar
});
assert_eq!(15, closure(5));
}
#[test]
fn multiple_capture() {
let mut string = String::from("string");
let x = 5;
let mut y = 10;
let mut closure = closure!(move string, ref x, ref mut y || {
string.push_str(" moved");
assert_eq!("string moved", &string);
assert_eq!(15, *x + *y);
});
closure();
}
#[test]
fn multiple_capture_w_args() {
#[inline]
fn take_closure(closure: impl FnOnce(String) -> String) {
let string = String::from("First");
let result = closure(string);
assert_eq!("First Second Third", &result);
}
let second = String::from("Second");
let third = String::from("Third");
let closure = closure!(move second, move third |first| {
format!("{} {} {}", first, second, third)
});
take_closure(closure);
}
}