#[macro_export(local_inner_macros)]
macro_rules! closure {
(@inner move $($ids:ident).+ , $($tail:tt)*) => {
let $crate::__extract_last_ident!($($ids).+) = $($ids).+;
closure!(@inner $($tail)*)
};
(@inner move mut $($ids:ident).+ , $($tail:tt)*) => {
let $crate::__extract_last_ident!(mut $($ids).+) = $($ids).+;
closure!(@inner $($tail)*)
};
(@inner ref $($ids:ident).+ , $($tail:tt)*) => {
let $crate::__extract_last_ident!($($ids).+) = & $($ids).+;
closure!(@inner $($tail)*)
};
(@inner ref mut $($ids:ident).+ , $($tail:tt)*) => {
let $crate::__extract_last_ident!($($ids).+) = &mut $($ids).+;
closure!(@inner $($tail)*)
};
(@inner $fn:ident $($ids:ident).+ , $($tail:tt)*) => {
let $crate::__extract_last_ident!($($ids).+) = $($ids).+.$fn();
closure!(@inner $($tail)*)
};
(@inner $fn:ident mut $($ids:ident).+ , $($tail:tt)*) => {
let $crate::__extract_last_ident!(mut $($ids).+) = $($ids).+.$fn();
closure!(@inner $($tail)*)
};
(@inner , $($tail:tt)*) => {
closure!(@inner $($tail)*)
};
(@inner $($closure:tt)*) => {
$crate::__assert_closure!($($closure)*);
move $($closure)*
};
($($args:tt)*) => {{
closure! { @inner $($args)* }
}};
}
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __extract_last_ident {
($last:ident) => { $last };
(mut $last:ident) => { mut $last };
($ignore:ident.$($tail:ident).+) => { $crate::__extract_last_ident!($($tail).+) };
(mut $ignore:ident.$($tail:ident).+) => { $crate::__extract_last_ident!(mut $($tail).+) };
}
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! __assert_closure {
(| $($any:tt)*) => {};
(|| $($any:tt)*) => {};
(move $($any:tt)*) => { compile_error!("keyword `move` not permitted here") };
($($any:tt)*) => {
compile_error!(concat!(
"the supplied argument is not a closure: `", stringify!($($any)*), "`")
);
};
}
#[cfg(test)]
mod test {
use crate::closure;
struct Foo {
bar: Bar,
}
#[derive(PartialEq, Eq)]
struct Bar {
baz: i32,
}
impl Foo {
fn new(baz: i32) -> Self {
Foo { bar: Bar { baz } }
}
fn consume(self) -> Box<dyn Fn(i32) -> bool> {
Box::new(closure!(move self.bar.baz, |expected| baz == expected))
}
fn borrow(&self) -> Box<dyn Fn(i32) -> bool + '_> {
Box::new(closure!(ref self.bar.baz, |expected| *baz == expected))
}
}
#[test]
fn no_capture_one_line() {
let closure = closure!(|| 5 * 5);
assert_eq!(closure(), 25);
}
#[test]
fn no_capture_with_arg() {
let closure = closure!(|x| x * x);
assert_eq!(closure(5), 25);
}
#[test]
fn no_capture_with_arg_and_type_hint() {
let closure = closure!(|x: usize| x * x);
assert_eq!(closure(5), 25);
}
#[test]
fn no_capture_with_arg_and_return_type() {
let closure = closure!(|x: usize| -> usize { x * x });
assert_eq!(closure(5), 25);
}
#[test]
fn no_capture_with_return_type() {
let closure = closure!(|| -> &str { "result" });
assert_eq!(closure(), "result");
}
#[test]
fn capture_by_move() {
let string = "move".to_string();
let closure = closure!(move string, || string.len());
assert_eq!(closure(), 4);
}
#[test]
fn capture_by_ref() {
let var = -1;
let closure = closure!(ref var, || *var == -1);
assert!(closure());
}
#[test]
fn capture_by_ref_mut() {
let mut var = -1;
closure!(ref mut var, || *var *= -1)();
assert_eq!(var, 1);
}
#[test]
fn capture_nested_by_move() {
let foo = Foo::new(-1);
let closure = closure!(move foo.bar, || bar == Bar { baz: -1 });
assert!(closure());
}
#[test]
fn capture_nested_by_ref() {
let foo = Foo::new(-1);
let closure = closure!(ref foo.bar, || *bar == Bar { baz: -1 });
assert!(closure());
}
#[test]
fn capture_nested_by_ref_mut() {
let mut foo = Foo::new(-1);
closure!(ref mut foo.bar.baz, |add| *baz += add)(2);
assert_eq!(foo.bar.baz, 1);
}
#[test]
fn capture_nested_with_self_by_move() {
let foo = Foo::new(-1);
let closure = foo.consume();
assert!(closure(-1));
}
#[test]
fn capture_nested_with_self_by_ref() {
let foo = Foo::new(-1);
let closure = foo.borrow();
assert!(closure(-1));
}
#[test]
fn capture_multiple_mixed() {
let borrow = 1;
let mut borrow_mut = 1;
let string = "move".to_string();
let closure = closure!(ref borrow, ref mut borrow_mut, move mut string, || {
assert_eq!(*borrow, 1);
*borrow_mut -= 1;
string.push_str("d back");
string
});
assert_eq!(&closure(), "moved back");
}
#[test]
fn capture_by_clone() {
use std::rc::Rc;
let rc = Rc::new(Foo::new(0));
let closure = closure!(clone rc, |expected| -> bool {
rc.bar.baz == expected && Rc::strong_count(&rc) == 2
});
assert!(closure(0));
}
#[test]
fn capture_by_fn_ident() {
let string = "string";
let closure = closure!(to_string string, || {
let mut owned: String = string;
owned.push_str(", now owned");
owned
});
assert_eq!(closure(), "string, now owned");
}
}