--- title: Explicit annotation on captures tags: Draft --- - Feature Name: (fill me in with a unique ident, `my_awesome_feature`) - Start Date: (fill me in with today's date, YYYY-MM-DD) - RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary [summary]: #summary * Permit closures to be annotated with `#[captures]` annotation, which will explicit list the captured variables. # Motivation [motivation]: #motivation Closures in Rust have traditionally determined the set of captured variables, and the way that those variables are captured, implicitly via analysis. However, there are some cases where explicitly citing the set of captures is useful. This is particularly true as we consider how Rust will feel after RFC 2229 is implemented. ## Primary motivation: preserving semantics in Rust 2021 ### RFC 2229 is changing closure semantics [RFC 2229] is changing how closure capture works in Rust 2021. In Rust 2018, closures always captured an entire local variable, even if they only referenced a small part of it. In Rust 2018 and before, for example, constructing the closure `c` would take ownership of `x` in its entirety, even though it only uses `x.1`: ```rust let x = (MyType::new(), String::new()); let c = move || println!("{:?}", x.1); ``` Thanks to [RFC 2229], in Rust 2021 and beyond, constructing `c` will only take ownership of `x.1`. `x.0` will remain owned by the creating stack frame. Making captures more precise usually matches what the user expects, but it can change the semantics of programs. In the example above, `x.0` would be dropped at different times in Rust 2018 and Rust 2021. In Rust 2018, `c` owns `x`, so `x.0` will be dropped when `c` is dropped. In Rust 2021, `x.0` remains owned by the creating stack frame, and hence it will be freed when `x` is dropped. RFC 2229 can also cause code not to compile. One example is the snippet reported in [rust-lang/project-rfc-2229#29]: [rust-lang/project-rfc-2229#29]: https://github.com/rust-lang/project-rfc-2229/issues/29 ```rust= fn assert_panics<F>(f: F) where F: FnOnce() { let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(move || { f.0() }); if let Ok(..) = result { panic!("diverging function returned"); } } ``` This code fails to compile under [RFC 2229] because the closure captures `f.0` and not `f`, but `f.0` is of type `F` and `F` is not `UnwindSafe`. Similar examples could occur with other auto traits, like `Send` and `Sync`. ### Migrating to Rust 2021 without this RFC It is possible to migrate to Rust 2021 without changing the order in which code runs by modifying the body of the closure. In this case, one could insert a `let _ &x` statement, like this: ```rust= let x = (MyType::new(), String::new()); let c = move || { let _ = &x; println!("{:?}", x.1); }; ``` This `let` statement is simply dropping a reference, so it has no effect, but it does mean that the closure references all of `x` and hence the closure `c` will capture `x` in its entirety in both Rust 2018 and Rust 2021. [RFC 2229]: https://github.com/rust-lang/rust/issues/53488 The `let` statement works, but it is confusing. Most readers won't really understand what purpose it serves. It's also non-obvious -- it took those of us in the RFC 2229 Project Group quite some time to realize that dropping a reference was the correct transformation that "always works" to enable the older semantics. **It's worth pointing out that this situation is not specific to migration:** Starting in Rust 2021, users will at times want to ensure that the closure captures a different place expression than the one used directly within the closure. We want to ensure that, when they ask how to do this, we have a good answer. ### Migrating to Rust 2021 with this RFC Under the semantics proposed in this RFC, closures can be annotated with a `#[captures]` annotation that will specify their captured variables explicitly. This means that our running example could be written: ```rust= let x = (MyType::new(), String::new()); let c = #[captures(x)] || println!("{:?}", x.1); ``` Annotations on expressions are already accepted by the compiler, but under a feature gate. This annotation clearly states that the closure captures `x`. The example from [rust-lang/project-rfc-2229#29] could be rewritten as follows: ```rust= fn assert_panics<F>(f: F) where F: FnOnce() { let f = panic::AssertUnwindSafe(f); let result = panic::catch_unwind(#[captures(f)] || { f.0() }); if let Ok(..) = result { panic!("diverging function returned"); } } ``` ## Secondary motivation: ergonomics Rust's rules for inferring closure captures sometimes work quite well, but some cases that users would like to express can be difficult. These scenarios are not solved by this RFC, but the explicit captures annotation leaves room to manage such cases in the future. ### Explicitly specifying the capture mode For each captured variable or place, the compiler currently automatically infers whether to capture by move, by shared reference (`&`), or by mutable reference (`&mut`). This is determined based on how the value is used within the closure. But people sometimes want to explicitly annotate the desired capture mode. Currently, the only want to do this is to use a move closure that captures references explicitly. There is a pattern for this. For example, the closure `c` explicitly captures `x` by value, `y` by shared reference, and `z` by mutable reference: ```rust= let c = { let x = x; let y = &y; let z = &mut z; move || /* code that uses `x`, `y`, and `z` */ }; ``` This pattern is very expressive, but it has some downsides: * First and foremost, it is non-obvious. Most people don't realize this is an option and instead simply get frustrated. * Some folks consider it an eyesore. * It *looks* like a complete capture list, but the syntax doesn't actually prevent the closure from capturing additional variables beyond `x`, `y`, and `z`. Under this proposal, one could write: ```rust= let c = #[captures(x, &y, &mut z)] || { /* code that uses `x`, `y`, and `z` */ }; ``` This would be equivalent to the previous code, except that it would be an error if the closure were to reference values apart from `x`, `y`, or `z`. If one wanted the ability to reference additional values, one could do that with the `..` annotation: ```rust= let c = #[captures(x, &y, &mut z, ..)] || { /* code that uses `x`, `y`, and `z`, and potentially other variables besides */ }; ``` ### Capturing clones Particularly when working with types like `Arc<T>`, many users would like to capture clones of the `Arc` and not the `Arc` itself. This can be accommodated by the same pattern as in the previous example, but it shares the same downsides: ```rust= let x = Arc::new(22); let c = { let x = x.clone(); move || /* code that uses `x` */ }; ``` Under this proposal, one could write: ```rust= let c = #[captures(x.clone())] || { /* code that uses `x` */ }; ``` ## Tertiary motivation: clarity Writing large closures can sometimes be convenient, because they are able to access a number of variables from the surrounding state of the function, but they can also obscure precisely what state the closure depends on. This was in fact the original motivation for disallowing nested functions from accessing their surrounding state, as well. As an example, consider this closure extracted rather at random from the rustc codebase ([source](https://github.com/rust-lang/rust/blob/3ad1e4dfedf2c1b3670c90a71ac0aaf0d9d0416a/compiler/rustc_ast_lowering/src/expr.rs#L1244-L1274)), and see if you can quickly ascertain what values it uses from the surrounding function: ```rust= let lower_reg = |reg| { Some(match reg { InlineAsmRegOrRegClass::Reg(s) => asm::InlineAsmRegOrRegClass::Reg( asm::InlineAsmReg::parse( sess.asm_arch?, |feature| sess.target_features.contains(&Symbol::intern(feature)), &sess.target, s, ) .map_err(|e| { let msg = format!("invalid register `{}`: {}", s.as_str(), e); sess.struct_span_err(*op_sp, &msg).emit(); }) .ok()?, ), InlineAsmRegOrRegClass::RegClass(s) => { asm::InlineAsmRegOrRegClass::RegClass( asm::InlineAsmRegClass::parse(sess.asm_arch?, s) .map_err(|e| { let msg = format!( "invalid register class `{}`: {}", s.as_str(), e ); sess.struct_span_err(*op_sp, &msg).emit(); }) .ok()?, ) } }) }; ``` Now compare to the following: ```rust= let lower_reg = #[captures(sess, op_sp)] |reg| { ... }; ``` ## Quaternary motivation: explicitness As a general rule (admittedly one inconsistently followed), we prefer to provide users with a convenient syntax that also has a fully explicit alternative. This gives users the ability to write out the details when they matter or are worth commenting. It is useful when debugging confusing compiler error messages. It is also useful when learning, as one can first learn the convenient syntax, then come to understand the details via the explicit notation, and finally return to the implicit, convenient syntax. Therefore, this RFC has generally tried to provide an explicit capture syntax that is at least as expressive as the implicit captures that we will support with RFC 2229. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation Closures in Rust are written `|| expr`. Closures may reference variables (or parts of variables) from the surrounding scope; this is called "capturing" those variables. When the closure is created, those captured values (or borrowed references to those captured values) are stored into the closure for use when the closure is called. ## Closures are a desugaring A closure expression like `|| expr` desugars into the pair of a struct and a set of impls. The struct stores the captured values that are contained within the closure. The impls implement some subset of the Rust function traits (`Fn`, `FnMut`, and `FnOnce`), along with a few other select traits. Example: ```rust= let x = 22; let y = 44; let c0 = || x + y; ``` The closure `c0` reads `x` and `y` from its creator. As explained in detail in the next section, `c0` will capture those variables by shared reference. This means that we can desugar the example to something roughly equivalent to the following: ```rust= struct ClosureStruct<'x, 'y> { x: &'x i32, y: &'y i32, } let x = 22; let y = 44; let c0 = ClosureStruct { x: &x, y: &y, }; ``` Because it only performs reads, This closure may safely be called multiple times. Therefore, the compiler would generate a `Fn` impl that contains the body `x+y` of the closure. This body is translated so that references to the captured variables go through `self`: ```rust= impl<'x, 'y> Fn<()> for ClosureStruct<'x, 'y> { fn call(&self, args: ()) -> i32 { *self.x + *self.y } } ``` There would also be `FnMut` and `FnOnce` impls that forward to this `Fn` impl. If the closure performed mutations to the captured variables, then the function body would be in the `FnMut` impl (and there would be no `Fn` impl), and if it performed moves from the captured variables, there would be a `FnOnce` impl. ```rust impl<'x, 'y> FnMut<()> for ClosureStruct<'x, 'y> { fn call_mut(&mut self, args: ()) -> i32 { Self::call(&*self, args) } } impl<'x, 'y> FnOnce<()> for ClosureStruct<'x, 'y> { type Output = i32; fn call_once(mut self) -> i32 { Self::call_mut(&mut self, args) } } ``` ## The default: inferred captures When you simply write a closure with `||`, the captures on that closures are inferred based on the variables that the closure actually uses. For example, the closure `c1` is inferred to capture the variables `m`, `sr`, and `mr`, and `t.0`: ```rust= let m = String::new(); let sr = 22; let mr = 44; let t = (22, 44, 66); let c1 = || { drop(m); mr = sr + 1 + t.0; }; ``` As the example of `t.0` shows, closures sometimes capture just a part of a variable. We call expressions like `t.0` "place expressions", or just "places" for short; this indicates that they name some "place in memory" (in this case, the 0th field of the tuple `t`). ### Capture mode for inferred captures For each captured place, there is also a "capture mode". This mode is determined by the compiler based on how the place is used: * If the place `p` is **moved**, then the capture is "by move". * Example: `c1` captures `m` by move, because it invokes `drop(m)`. * If the place `p` is **mutated** or borrowed with `&mut`, then the capture is "by mut reference". * Example: `c1` captures `mr` by mutable reference, because it assigns to `mr`. * If the place `p` is only **read** or borrowed with `&`, then the capture is "by shared reference". * Example: `c1` captures the place `sr` and the place `t.0` by shared reference. #### Inferred capture modes do not affect the type as seen by users The capture mode only affects the **desugaring** of the closure, and doesn't affect the types of captured variables as seen by users. For example, the variable `sr` is inferred to be captured by shared reference, and hence the desugared closure will have a field `sr: &i32` that stores a `&i32` value. However, the expression `sr` in the closure body is translated to `*self.sr`, so the type as seen by the user is still `i32`. ### Move closures: forcing "move" mode Inferred capture modes work well for closures that do not escape the current function. When a closure will be returned, however, the closure cannot store references into the creating function's stack frame. In such cases, users can annotate the closure with the `move` keyword. This forces all variables to be captured "by move". The closure `c2` therefore captures `sr`, `mr`, and `t.0` by move, even though they are used in different ways: ```rust= let m = String::new(); let sr = 22; let mr = 44; let t = (22, 44, 66); let c2 = move || { drop(m); mr = sr + 1 + t.0; }; ``` ## Explicit captures annotations Even with the `move` keyword, there are some cases where inferred captures are not convenient. This could be for many reasons: * You wish to capture a clone of the value, and not the value itself. * You wish to be explicit about what variables your closure uses and how. * You wish to override the inferred mode with which a variable will be captured. * The most common reason to override is to force a move capture, so that the variable can be returned from the stack frame, but there could be other reasons. For example, perhaps the closure does not need to escape the entire function (and hence doesn't have to be fully move) but does need to escape a specific block (and hence can't capture values owned by that block by reference). * You wish to capture a broader path than the one which is actually named in the closure. For example, maybe your closure uses `t.0` only but you actually wish to capture all of `t`. To cover these cases, Rust permits an optional `#[captures]` annotation. This annotation takes the place of the `move` keyword (the two cannot be combined). It accepts a variety of notations. Here are some examples: * `#[captures(x, y)]` * Indicates that closure captures `x` and `y` "by move". * Using any variables apart from `x` and `y` will be an error. * `#[captures(x, y, ..)]` * Indicates that closure captures `x` and `y` "by move". * The closure can use other variables apart from `x` and `y`. Their values will be inferred as normal. * `#[captures(x, y, move..)]` * Indicates that closure captures `x` and `y` "by move". * The closure can use other variables apart from `x` and `y`. Their values will be inferred to move. * `#[captures(x.clone())]` * The closure captures `x.clone()` and calls it `x` internally. * No other captures are permitted. * `#[captures(x.unwrap())]` * The closure captures `x.unwrap()` and calls it `x` internally. * No other captures are permitted. * `#[captures(x.as_ref())]` * The closure captures `x.as_ref` and calls it `x` internally. * No other captures are permitted. * `#[captures(z = x.clone())]` * The closure captures `x.clone()` and calls it `z` internally. * No other captures are permitted. * `#[captures(z = x.0)]` * The closure captures `x.0` and calls it `z` internally. * No other captures are permitted. * `#[captures(z = x.0.clone())]` * The closure captures a clone of `x.0` and calls it `z` internally. * No other captures are permitted. * `#[captures(..)]` * Equivalent to no capture annotation at all. * `#[captures(move..)]` * Equivalent to a `move` closure. ### Grammar for closure captures The "captures clause" for a closure has the following grammar. The notation `X..` indicates a comma separated list of `X` values. The notation `Nonterminal<X>` indicates a macro where `X` can be substituted for different terminals or nonterminals. ``` CaptureClause = `#` `[` `captures` `(` CaptureClauseElement.. `)` `]` CaptureClauseElement = Expr<Identifier> | Identifier = Expr<Place> | `..` (*) | `move` `..` (*) Expr<Op> = Place | `&` Place | `&mut` Place | Place `.` Identifier `(` `)` Place = Identifier | Place `.` Identifier | `*` Place (*) These elements must appear last in the list. ``` When the `CaptureClauseElement` is simply an `Expr`, it is equivalent to `X = Expr` where `X` is the name of the local variable that roots the `Place` found in `Expr`. The `CaptureClauseElement` values of `..` and `move..` indicate that the closure may capture additional values. These two elements The modes of those values will either be inferred or be specified as by move, respectively. ### Differences between inferred and explicit capture clauses Using an explicit capture clause does affect the types of captured values in ways that are different from the equivalent inferred captures. For example, consider this closure: ```rust= let x = 1; let y = 2; let c_inferred = || x + y; ``` If we use explicit annotation to make the results of inference explicit, the types of `x` and `y` change to `&i32` and `&i32`, to reflect their desugared values: ```rust= let x = 1; let y = 2; let c_explicit = #[captures(&x, &y)] || *x + *y; ``` The code generated for `c_inferred` and `c_explicit` is completely equivalent, however. ### Understanding explicit annotations as a desugaring to inferred closures An expression like `#[captures(z = x.clone())] || something(z)` can be understood as a desugaring to a block and a `move` closure: ```rust= let c = { let z = x.clone(); move || { drop(&z); something(z) } }; ``` The only difference is that, with this desugaring, the closure could capture additional values beyond the clone of `x`, but the explicit `#[captures]` annotation cannot. ## Relationship of captures to the `Fn` traits The set of captured values and the modes of those captured values (by move, by ref, by mut ref) is unrelated to the `Fn` traits that a closure implements. The traits that a closure implements are determined by how it uses the values it captures, not the sources of those captured values: * If the closure moves one of its captured values, it can only implement `FnOnce`. * Otherwise, if the closure mutates one of its captured values or takes a `&mut` reference to it, it implements `FnMut` and `FnOnce`. * Otherwise the closure only reads its captured values, so it implements `Fn`, `FnMut`, and `FnOnce`. Note in particular that a `move` closure may implement any of the `Fn` traits. The following `move` closure, for example, implements `Fn`, `FnMut`, and `FnOnce`; this is because, despite taking ownership of a `String`, it only reads from that value: ```rust= let x = "Hello, World!".to_string(); let c = move || x.clone(); ``` # Reference-level explanation [reference-level-explanation]: #reference-level-explanation ## Implementation stategy: desugaring The RFC can be implemented by internally desugaring closures with `#[captures]` annotations within the compiler into the desugaring given at the end of the "guide" section. We will have to add an annotation to closure expressions to indicate that they cannot capture anything beyond a fixed set of variables. ## Implementation stategy: desugaring Name resolution is currently done on the AST and it will have to be The compiler already has facilities to allow desugarings like the one above without leaking them into diagnostics. ## Error reporting When users attempt to use values that are not in the captures list, we should report those attempts in a friendly fashion and suggest adding them to the capture list. ## RFC 2229 migration RFC 2229 migration will automatically insert annotations like `#[captures(a, b, ..)]`, where `a` and `b` are variables that were previously captured by move in their entirety, but which would only be partially captured in the new model, and this change may cause a significant destructor to change when it runs, or change which auto traits are implemented. # Drawbacks [drawbacks]: #drawbacks Language is larger. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives ## Why permit fields in the list of captured closures? We wanted the explicit capture syntax to be as expressive as the inferred captures supported by RFC 2229. In other words, under RFC 2229 the user can write a closure like ```rust= let c = || println!("{}", x.0.1); ``` and this could be expressed as the following with explicit captures ```rust= let c = #[captures(z = x.0.1)] || println!("{}", z); ``` Without the ability to capture places like `x.0.1` ## Why require explicit names when capturing places like `x.0.1`? When the place has fields (e.g., `x.0.1`) the grammar requires an explicit name. This is to avoid user confusion; there is no obvious identifier we can use within the closure to express `x.0.1`. We could try to say that users continue to refer to a complex place like `x.0.1` as `x.0.1` within the closure (but they would get an error if they try to capture some other field, e.g. by referencing `x.1`). One challenge is that we would also need a syntax for "inferred capture modes" (e.g., a way to capture `x.0` "by reference" without changing its type) for this to work. This is something that we could explore in the future as a possible extension if it proved worthwhile. ## Why not have an explicit notation equivalent to inferred capture modes? As explained in the RFC, inferred capture modes don't change the observed type of the variable -- so in the following closure, `x` retains the type `i32` and not `&i32`, even though it is captured by shared reference from its environment: ```rust= let c = || x + 1; ``` The explicit capture variant of this `#[captures(&x)]` would change the type of `x`. We chose to do this because there is no obvious syntax we can use that means "same type as `x` but with a shared reference mode". Modes are not generally a concept that exists in Rust outside of implicit closure captures (unlike, say, C++ references, which have a more "mode-like" character to them). We considered the notation `#[captures(ref x)]` for this, but the behavior of `ref x` in match expressions is that the type of `x` is still `&i32`, so this would be inconsistent (moreover, with default match bindings, the value of `ref x` is questionable). ## Why would anyone want to require the capture list to be complete? As described in the motivation, this can be useful for documenting the set of variables captured by a closure without requiring users to manually inspect the closure body. It is also avoids surprises where the captures list "appears" to be complete but in fact is not -- that is, users skimming code may be surprised to learn that a closure like ```rust= #[captures(a, b, c, d, e)] || { ... } ``` in fact also captures `f`. ## Why not just migrate for RFC 2229 with `let _ = &x` statements? While inserting `let _ = &x` is an acceptable hack for RFC 2229 migration, the fact remains that in the future some code will legitimately wish to capture an entire variable while only explicitly using some part of it. Lacking capture clauses, we have no idiomatic or natural way to express that. ## Why not use `move(CC)` as the syntax instead of `#[captures(CC)]`? Syntax like `move(a, b, c) || a + b * c` has certain advantages: * It feels more "built-in" than an attribute. * Something like `move(&x)` rather clearly expresses that the closure captures a reference to `x`. * There are no hygiene issues that we have to resolve around the use of attributes. It has some disadvantages as well: * It suggests that the closure is a "move" closure, and hence there is no satisfying way to express "and the remaining uses are inferred as normal". This means that `move(a, b)` would be more difficult to use for migrating non-move closures for RFC 2229 migration; we would either have to insert `let _ = &a` statements explicitly in such code, and all such code in the future would have to be written as move closures. * `move(a, b) || c` looks rather like a function call and the boolean `||` operator. There are some other alternatives: * `move[a]` or `move{a}` look less like a function call, but they are not consistent visually with `pub(super)` and the like. ## Why not use an lint for captures outside of the captures list? We could remove the `..` and `move..` notation and instead have a lint if the closure uses variables not in its explicit capture list. However, we would need to have some way for users to disable the lint and indicate that they did not intend for the closure list to be complete. ## Why not make the captures list incomplete, and require an explicit notation if it is complete? We could do that, but it is not how most things in Rust work. We generally use the `..` operator to indicate incomplete lists: * in pattern matching, when listing fields like `Foo { a, b, .. }`; * when creating a struct, to indicate the base value `Foo { a, b, ..c }` ## Why not implement `#[captures]` using a procedural macro? Using a procedural macro and a desugaring always allows the closure to capture additional values beyond those in the `captures` list. Procedural macros are also unstable in this position, though if it is shipped as part of std we could make an exception. Some future extensions listed under the "future possibilities" lint could not be done as procedural macros. ## If it is a procedural macro, couldn't we let it evolve in the wild before deciding whether to add it to std? Yes, we could, but (a) procedural macros in that position are unstable; (b) we need to address RFC 2229 migration; and (c) capturing clones of values and things is fairly useful. ## To support RFC 2229 migration, we only need to support capturing entire variables. Why do the rest? We could indeed do a more limited version to start -- and likely we would, just to ease implementation. RFC 2229 specifically requires only clauses of the form `#[captures(a, b, c, ..)]`. The motivation for including more in the RFC was: * captures clauses should be able to express all the things that the inferred syntax can express * this brings us closer to a principle of "things can be made explicit, if you care to", which we generally try to adhere to when we can * the question of how to capture clones comes up fairly regularly and it would be nice to have a more obvious answer for how to address it * a general prefernce to outline a "well rounded destination", versus designing "half a feature" * specifically, capturing only variables doesn't feel like what I would want as a user ## Why not permit arbitrary expressions, like C++? We could in principle permit `x = Expr` for arbitrary expressions in the captures list. We have chosen not to do so for now. This would be a possible future development if it proves desirable. We might also permit things like `x = y[3]` or `y.foo(3)`. We have chosen not to do this because it would require deciding what sorts of expressions can appear as indices or arguments, respectively. ## What about a `captures!` macro in the closure body? One initial idea for the RFC 2229 migration was to add a `captures!(x)` macro that expanded to `drop(&x)`. This would permit one to write closures as follows: ```rust= let c = || { captures!(x); drop(x.0); }; ``` This addresses the concern with RFC 2229 migration (in particular, it is more self-documenting than `drop(&x)`), but leaves no path to address issues like capturing a clone of a value. ## Doesn't the `#[captures]` syntax feel "bolted on"? We are open to alternatives, but the combination of a fully inferred default and a `#[captures]` annotation for explicit document has a number of advantages: * It is relatively easy to deduce what `#[captures]` means, and easy to search the documentation if you are unsure. * The default, fully inferred `|| closure` syntax remains extremely lightweight. * It can be extended to named function items in the future if we choose to do so (see the Future Directions section). # Prior art [prior-art]: #prior-art ## C++ lambda expressions and capture clauses C++ contains [lambda expressions] with an explicit capture clause (here is [documentation] from Microsoft): ```cpp= std::sort(x, x + n, // Lambda expression begins [](float a, float b) { return (std::abs(a) < std::abs(b)); } // end of lambda expression ); ``` [lambda expressions]: https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-160 [documentation]: https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-160#capture-clause C++ capture clauses are similar to those proposed in this RFC: * They use the mode specifier `=` to indicate "by value". * They use the mode specifier `&` to indicate "by reference". * They permit one to write `[=]` to capture all named variables by value, or `[&]` to capture all names variables by reference. * C++14 also introduced [generalized capture](https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-160#capture-clause) which is similar to the `x = Expr` synyax (but permits arbitrary expressions). # Unresolved questions [unresolved-questions]: #unresolved-questions * None. # Future possibilities [future-possibilities]: #future-possibilities We might consider extending this annotation, or adding another, to permit users to specify `Fn`, `FnMut`, and `FnOnce` explicitly. We might consider permitting this annotation on `async { }` blocks. We may add also async closures in the future. Both of these extensions ought to be straightforward. We could permit anonymous capture clauses of places with fields (e.g., `#[captures(x.0)]`) but give an error if the closure attempts to access other fields (e.g., `x.1`). This could not be done as a procedural macro. Furthermore, this would not work with modes other than by value, so some more exploration would be required. Currently, we have no syntax for generic closures, and we have no syntax for closures that fully annotate all of their function parameter types and so forth. We could permit `#[captures]` annotations on nested functions as a way to 'opt-in' to using that syntax to express closures: ```rust= fn outer_function(context: &Context, value: A) { #[captures(context)] fn helper<T: Debug>(value: T) { context.do_something(value); } helper(22); helper(44); } ``` We might consider permitting named captures to be accessed as fields from outside the closure, perhaps with a `pub` annotation. Example: ```rust= let x = 22; let c = #[captures(pub x)] x + 1; let d = c.x; // equals `x` ```