├── .gitignore ├── rustfmt.toml ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /target/ 3 | Cargo.lock 4 | **/*.rs.bk 5 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_field_init_shorthand = true 2 | use_small_heuristics = "Max" 3 | use_try_shorthand = true 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | - 1.28.0 8 | 9 | script: 10 | - cargo build 11 | - cargo build --release 12 | - cargo build --no-default-features 13 | - cargo build --release --no-default-features 14 | - cargo test 15 | - cargo test --release 16 | 17 | matrix: 18 | allow_failures: 19 | - rust: nightly 20 | fast_finish: true -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "closure" 3 | description = "A macro for capturing variables on a per variable basis." 4 | version = "0.3.0" 5 | authors = ["oliver "] 6 | license = "MIT" 7 | readme = "README.md" 8 | repository = "https://github.com/oliver-giersch/closure.git" 9 | edition = "2018" 10 | keywords = ["closure", "capture", "macro"] 11 | exclude = [".travis.yml"] 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Oliver Giersch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Looking For Maintainers 2 | 3 | This crate is no longer maintained. Feel free to contact the author if you'd like to continue developing it. 4 | 5 | # closure! - A macro for individually capturing variables 6 | 7 | [![Latest version](https://img.shields.io/crates/v/closure.svg)]( 8 | https://crates.io/crates/closure) 9 | [![Documentation](https://docs.rs/closure/badge.svg)](https://docs.rs/closure) 10 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)]( 11 | https://github.com/oliver-giersch/closure) 12 | 13 | This crate provides a macro which lets you write closures that can capture 14 | individually either by moving, referencing, mutably referencing of cloning. 15 | 16 | ## Usage 17 | 18 | Start by adding an entry to your `Cargo.toml`: 19 | 20 | ```toml 21 | [dependencies] 22 | closure = "0.3.0" 23 | ``` 24 | 25 | Then you can write closures like so: 26 | 27 | ```rust 28 | use closure::closure; 29 | 30 | let string = "move".to_string(); 31 | let x = 10; 32 | let mut y = 20; 33 | let rc = Rc::new(5); 34 | 35 | let closure = closure!(move string, ref x, ref mut y, clone rc, |arg: i32| { 36 | ... 37 | }); 38 | 39 | ``` 40 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A macro for capturing variables on a per variable basis. 2 | //! 3 | //! With this macro it is possible to specifically designate which variables 4 | //! will be captured by which method in a designated *capture list*. 5 | //! Variables can be either specified to be moved, referenced, mutably 6 | //! referenced or transformed using an arbitrary method identifier (e.g., 7 | //! `clone`). 8 | //! Any variables not specifically designated will be moved by default. 9 | //! 10 | //! The syntax for each type of capture type is: 11 | //! - `move var` (moves `var` into the closure) 12 | //! - `ref var` (borrows `var`) 13 | //! - `ref mut var` (mutably borrows `var`) 14 | //! - `$IDENT var` (transforms `var` where $IDENT is any identifier for a method 15 | //! with a `self` receiver and no further arguments) 16 | //! 17 | //! ## Move Binding 18 | //! 19 | //! To capture a variable by moving it into the closure, use `move` or 20 | //! `move mut` to create a mutable binding: 21 | //! 22 | //! ``` 23 | //! # use closure::closure; 24 | //! let first = "first".to_string(); 25 | //! let second = "second".to_string(); 26 | //! 27 | //! let closure = closure!(move first, move mut second, || { 28 | //! // creates an immutable `first` and a mutable `second` 29 | //! // binding... 30 | //! # assert_eq!(first, "first"); 31 | //! # second.clear(); 32 | //! # assert_eq!(second, ""); 33 | //! }); 34 | //! ``` 35 | //! 36 | //! ## Reference Binding 37 | //! 38 | //! To capture a variable by borrowing it in the closure, use `ref` or `ref mut` 39 | //! for a mutable borrow, respectively. 40 | //! 41 | //! ``` 42 | //! # use closure::closure; 43 | //! let mut a = 1; 44 | //! let b = 0; 45 | //! 46 | //! let mut closure = closure!(ref mut a, ref b, || { 47 | //! *a = 0; 48 | //! assert_eq!(*a, *b); 49 | //! }); 50 | //! # closure(); 51 | //! ``` 52 | //! 53 | //! Notice, that is also possible to capture named members of a `struct`, 54 | //! including in `struct` methods: 55 | //! 56 | //! ``` 57 | //! # use closure::closure; 58 | //! struct Foo { 59 | //! bar: i32, 60 | //! } 61 | //! 62 | //! impl Foo { 63 | //! fn print(&self) { 64 | //! // here a binding `let bar = &self.bar` will be 65 | //! // created for the closure 66 | //! closure!(ref self.bar, || println!("{}", bar))(); 67 | //! } 68 | //! } 69 | //! ``` 70 | //! 71 | //! This also applies to `move` captures, but the usual rules for destructuring 72 | //! apply. 73 | //! 74 | //! ## $IDENT-transform Binding 75 | //! 76 | //! Capturing a variable by an arbitrary identifier of a method with any `self` 77 | //! reciever (e.g., `self`, `&self`, `&mut self`, etc.) and no other arguments, 78 | //! creates a binding of the same name but the with the transformation method 79 | //! applied to the original variable. 80 | //! The most common use case for this type of capture is probably for calling 81 | //! `clone()` on a variable, but any method conforming to the aforementioned 82 | //! rules is also possible, such as `to_string`, `to_owned`, `into_iter`, etc. 83 | //! 84 | //! ``` 85 | //! # use closure::closure; 86 | //! let first = "first".to_string(); 87 | //! let second = "second".to_string(); 88 | //! 89 | //! let mut closure = closure!(clone first, clone mut second, || { 90 | //! // creates two bindings `first` and `second`, 91 | //! // the latter is mutable. 92 | //! println!("cloned: {}", first); 93 | //! second.clear(); 94 | //! # assert_eq!(second, ""); 95 | //! }); 96 | //! 97 | //! closure(); 98 | //! println!("the original {} and {} were not moved", first, second); 99 | //! ``` 100 | //! 101 | //! # Examples 102 | //! 103 | //! ## Spawning a Thread 104 | //! 105 | //! Instead of having to write: 106 | //! 107 | //! ``` 108 | //! use std::thread; 109 | //! use std::sync::{Arc, Barrier, Mutex}; 110 | //! 111 | //! let mutex = Arc::new(Mutex::new(Vec::new())); 112 | //! let barrier = Arc::new(Barrier::new(2)); 113 | //! 114 | //! let vector_clone = Arc::clone(&mutex); 115 | //! let barrier_clone = Arc::clone(&barrier); 116 | //! 117 | //! thread::spawn(move || { 118 | //! let mut vec = vector_clone.lock().unwrap(); 119 | //! vec.push(2); 120 | //! vec.push(3); 121 | //! vec.push(4); 122 | //! 123 | //! barrier_clone.wait(); 124 | //! }); 125 | //! 126 | //! barrier.wait(); 127 | //! let mut vec = mutex.lock().unwrap(); 128 | //! 129 | //! vec.push(1); 130 | //! assert_eq!(*vec, &[2, 3, 4, 1]); 131 | //! ``` 132 | //! 133 | //! Using `closure!` it becomes possible to avoid having to manually create 134 | //! bindings for each cloned `Arc`: 135 | //! 136 | //! ``` 137 | //! use std::thread; 138 | //! use std::sync::{Arc, Barrier, Mutex}; 139 | //! 140 | //! use closure::closure; 141 | //! 142 | //! let mutex = Arc::new(Mutex::new(Vec::new())); 143 | //! let barrier = Arc::new(Barrier::new(2)); 144 | //! 145 | //! thread::spawn(closure!(clone mutex, clone barrier, || { 146 | //! let mut vec = mutex.lock().unwrap(); 147 | //! vec.push(2); 148 | //! vec.push(3); 149 | //! vec.push(4); 150 | //! 151 | //! barrier.wait(); 152 | //! })); 153 | //! 154 | //! barrier.wait(); 155 | //! let mut vec = mutex.lock().unwrap(); 156 | //! 157 | //! vec.push(1); 158 | //! assert_eq!(*vec, &[2, 3, 4, 1]); 159 | //! ``` 160 | //! 161 | //! ## Moving cloned smart pointers into thread closures 162 | //! 163 | //! From the documentation of [`Condvar`][std::sync::Condvar]: 164 | //! 165 | //! ``` 166 | //! use std::sync::{Arc, Mutex, Condvar}; 167 | //! use std::thread; 168 | //! 169 | //! let pair = Arc::new((Mutex::new(false), Condvar::new())); 170 | //! let pair2 = pair.clone(); 171 | //! 172 | //! // Inside of our lock, spawn a new thread, and then wait for it to start. 173 | //! thread::spawn(move|| { 174 | //! let &(ref lock, ref cvar) = &*pair2; 175 | //! let mut started = lock.lock().unwrap(); 176 | //! *started = true; 177 | //! // We notify the condvar that the value has changed. 178 | //! cvar.notify_one(); 179 | //! }); 180 | //! 181 | //! // Wait for the thread to start up. 182 | //! let &(ref lock, ref cvar) = &*pair; 183 | //! let mut started = lock.lock().unwrap(); 184 | //! while !*started { 185 | //! started = cvar.wait(started).unwrap(); 186 | //! } 187 | //! ``` 188 | //! 189 | //! With `closure!`, the explicit declaration of `pair2` can be avoided: 190 | //! 191 | //! ``` 192 | //! use std::sync::{Arc, Mutex, Condvar}; 193 | //! use std::thread; 194 | //! 195 | //! use closure::closure; 196 | //! 197 | //! let pair = Arc::new((Mutex::new(false), Condvar::new())); 198 | //! 199 | //! // Inside of our lock, spawn a new thread, and then wait for it to start. 200 | //! thread::spawn(closure!(clone pair, || { 201 | //! let &(ref lock, ref cvar) = &*pair; 202 | //! let mut started = lock.lock().unwrap(); 203 | //! *started = true; 204 | //! // We notify the condvar that the value has changed. 205 | //! cvar.notify_one(); 206 | //! })); 207 | //! 208 | //! // Wait for the thread to start up. 209 | //! let &(ref lock, ref cvar) = &*pair; 210 | //! let mut started = lock.lock().unwrap(); 211 | //! while !*started { 212 | //! started = cvar.wait(started).unwrap(); 213 | //! } 214 | //! ``` 215 | //! 216 | //! ## Mixing move and reference captures without having to specifically declare 217 | //! the references which should not be moved 218 | //! 219 | //! ``` 220 | //! # use closure::closure; 221 | //! let move_string = "this string will be moved".to_string(); 222 | //! let mut ref_string = "this string will be borrowed".to_string(); 223 | //! 224 | //! let mut closure = closure!(move move_string, ref mut ref_string, || { 225 | //! ref_string.push_str(&move_string); 226 | //! //.. `move_string` is dropped at the end of the scope 227 | //! }); 228 | //! 229 | //! # closure(); 230 | //! # assert_eq!( 231 | //! # ref_string, 232 | //! # "this string will be borrowedthis string will be moved" 233 | //! # ); 234 | //! ``` 235 | //! 236 | //! Variable identifiers in the argument position (i.e., between the vertical 237 | //! lines) and return type specifications can also be used same as in regular 238 | //! closures. 239 | //! 240 | //! # Limitations 241 | //! 242 | //! Any closure passed to the macro will implicitly become a `move` closure, so 243 | //! even variables that don't appear in the capture list but are used in the 244 | //! closure itself will also be moved into it. 245 | 246 | /// A macro that allows specifying a capture list for a closure that is passed 247 | /// to the macro. 248 | /// 249 | /// See the [crate-level](index.html) docs for further information on syntax and 250 | /// examples. 251 | #[macro_export(local_inner_macros)] 252 | macro_rules! closure { 253 | (@inner move $($ids:ident).+ , $($tail:tt)*) => { 254 | let $crate::__extract_last_ident!($($ids).+) = $($ids).+; 255 | closure!(@inner $($tail)*) 256 | }; 257 | (@inner move mut $($ids:ident).+ , $($tail:tt)*) => { 258 | let $crate::__extract_last_ident!(mut $($ids).+) = $($ids).+; 259 | closure!(@inner $($tail)*) 260 | }; 261 | (@inner ref $($ids:ident).+ , $($tail:tt)*) => { 262 | let $crate::__extract_last_ident!($($ids).+) = & $($ids).+; 263 | closure!(@inner $($tail)*) 264 | }; 265 | (@inner ref mut $($ids:ident).+ , $($tail:tt)*) => { 266 | let $crate::__extract_last_ident!($($ids).+) = &mut $($ids).+; 267 | closure!(@inner $($tail)*) 268 | }; 269 | (@inner $fn:ident $($ids:ident).+ , $($tail:tt)*) => { 270 | let $crate::__extract_last_ident!($($ids).+) = $($ids).+.$fn(); 271 | closure!(@inner $($tail)*) 272 | }; 273 | (@inner $fn:ident mut $($ids:ident).+ , $($tail:tt)*) => { 274 | let $crate::__extract_last_ident!(mut $($ids).+) = $($ids).+.$fn(); 275 | closure!(@inner $($tail)*) 276 | }; 277 | (@inner , $($tail:tt)*) => { 278 | closure!(@inner $($tail)*) 279 | }; 280 | // matches on the actual closure (w/o move) 281 | (@inner $($closure:tt)*) => { 282 | $crate::__assert_closure!($($closure)*); 283 | move $($closure)* 284 | }; 285 | // macro entry point (accepts anything) 286 | ($($args:tt)*) => {{ 287 | closure! { @inner $($args)* } 288 | }}; 289 | } 290 | 291 | #[macro_export(local_inner_macros)] 292 | #[doc(hidden)] 293 | macro_rules! __extract_last_ident { 294 | ($last:ident) => { $last }; 295 | (mut $last:ident) => { mut $last }; 296 | ($ignore:ident.$($tail:ident).+) => { $crate::__extract_last_ident!($($tail).+) }; 297 | (mut $ignore:ident.$($tail:ident).+) => { $crate::__extract_last_ident!(mut $($tail).+) }; 298 | } 299 | 300 | 301 | #[macro_export(local_inner_macros)] 302 | #[doc(hidden)] 303 | macro_rules! __assert_closure { 304 | (| $($any:tt)*) => {}; 305 | (|| $($any:tt)*) => {}; 306 | (move $($any:tt)*) => { compile_error!("keyword `move` not permitted here") }; 307 | ($($any:tt)*) => { 308 | compile_error!(concat!( 309 | "the supplied argument is not a closure: `", stringify!($($any)*), "`") 310 | ); 311 | }; 312 | } 313 | 314 | #[cfg(test)] 315 | mod test { 316 | use crate::closure; 317 | 318 | struct Foo { 319 | bar: Bar, 320 | } 321 | 322 | #[derive(PartialEq, Eq)] 323 | struct Bar { 324 | baz: i32, 325 | } 326 | 327 | impl Foo { 328 | fn new(baz: i32) -> Self { 329 | Foo { bar: Bar { baz } } 330 | } 331 | 332 | fn consume(self) -> Box bool> { 333 | Box::new(closure!(move self.bar.baz, |expected| baz == expected)) 334 | } 335 | 336 | fn borrow(&self) -> Box bool + '_> { 337 | Box::new(closure!(ref self.bar.baz, |expected| *baz == expected)) 338 | } 339 | } 340 | 341 | #[test] 342 | fn no_capture_one_line() { 343 | let closure = closure!(|| 5 * 5); 344 | assert_eq!(closure(), 25); 345 | } 346 | 347 | #[test] 348 | fn no_capture_with_arg() { 349 | let closure = closure!(|x| x * x); 350 | assert_eq!(closure(5), 25); 351 | } 352 | 353 | #[test] 354 | fn no_capture_with_arg_and_type_hint() { 355 | let closure = closure!(|x: usize| x * x); 356 | assert_eq!(closure(5), 25); 357 | } 358 | 359 | #[test] 360 | fn no_capture_with_arg_and_return_type() { 361 | let closure = closure!(|x: usize| -> usize { x * x }); 362 | assert_eq!(closure(5), 25); 363 | } 364 | 365 | #[test] 366 | fn no_capture_with_return_type() { 367 | let closure = closure!(|| -> &str { "result" }); 368 | assert_eq!(closure(), "result"); 369 | } 370 | 371 | #[test] 372 | fn capture_by_move() { 373 | let string = "move".to_string(); 374 | let closure = closure!(move string, || string.len()); 375 | assert_eq!(closure(), 4); 376 | } 377 | 378 | #[test] 379 | fn capture_by_ref() { 380 | let var = -1; 381 | let closure = closure!(ref var, || *var == -1); 382 | assert!(closure()); 383 | } 384 | 385 | #[test] 386 | fn capture_by_ref_mut() { 387 | let mut var = -1; 388 | closure!(ref mut var, || *var *= -1)(); 389 | assert_eq!(var, 1); 390 | } 391 | 392 | #[test] 393 | fn capture_nested_by_move() { 394 | let foo = Foo::new(-1); 395 | let closure = closure!(move foo.bar, || bar == Bar { baz: -1 }); 396 | assert!(closure()); 397 | } 398 | 399 | #[test] 400 | fn capture_nested_by_ref() { 401 | let foo = Foo::new(-1); 402 | let closure = closure!(ref foo.bar, || *bar == Bar { baz: -1 }); 403 | assert!(closure()); 404 | } 405 | 406 | #[test] 407 | fn capture_nested_by_ref_mut() { 408 | let mut foo = Foo::new(-1); 409 | closure!(ref mut foo.bar.baz, |add| *baz += add)(2); 410 | assert_eq!(foo.bar.baz, 1); 411 | } 412 | 413 | #[test] 414 | fn capture_nested_with_self_by_move() { 415 | let foo = Foo::new(-1); 416 | let closure = foo.consume(); 417 | assert!(closure(-1)); 418 | } 419 | 420 | #[test] 421 | fn capture_nested_with_self_by_ref() { 422 | let foo = Foo::new(-1); 423 | let closure = foo.borrow(); 424 | assert!(closure(-1)); 425 | } 426 | 427 | #[test] 428 | fn capture_multiple_mixed() { 429 | let borrow = 1; 430 | let mut borrow_mut = 1; 431 | let string = "move".to_string(); 432 | 433 | let closure = closure!(ref borrow, ref mut borrow_mut, move mut string, || { 434 | assert_eq!(*borrow, 1); 435 | *borrow_mut -= 1; 436 | string.push_str("d back"); 437 | string 438 | }); 439 | 440 | assert_eq!(&closure(), "moved back"); 441 | } 442 | 443 | #[test] 444 | fn capture_by_clone() { 445 | use std::rc::Rc; 446 | 447 | let rc = Rc::new(Foo::new(0)); 448 | let closure = closure!(clone rc, |expected| -> bool { 449 | rc.bar.baz == expected && Rc::strong_count(&rc) == 2 450 | }); 451 | assert!(closure(0)); 452 | } 453 | 454 | #[test] 455 | fn capture_by_fn_ident() { 456 | let string = "string"; 457 | let closure = closure!(to_string string, || { 458 | let mut owned: String = string; 459 | owned.push_str(", now owned"); 460 | owned 461 | }); 462 | 463 | assert_eq!(closure(), "string, now owned"); 464 | } 465 | } 466 | --------------------------------------------------------------------------------