From d845bc3c28194c66357a8dde0ef5eed2e2f8db8a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sun, 8 Nov 2020 17:52:40 -0800 Subject: [PATCH 01/32] Add RangeFrom patterns --- src/patterns.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/patterns.md b/src/patterns.md index a27489fc78..763633c858 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -402,6 +402,8 @@ match tuple { > **Syntax**\ > _RangePattern_ :\ >    _RangePatternBound_ `..=` _RangePatternBound_ +> _RangeFromPattern_ :\ +>    _RangePatternBound_ `..` > > _ObsoleteRangePattern_ :\ >    _RangePatternBound_ `...` _RangePatternBound_ @@ -414,9 +416,10 @@ match tuple { >    | [_PathInExpression_]\ >    | [_QualifiedPathInExpression_] -Range patterns match values that are within the closed range defined by its lower and -upper bounds. For example, a pattern `'m'..='p'` will match only the values `'m'`, `'n'`, -`'o'`, and `'p'`. The bounds can be literals or paths that point to constant values. +Range patterns match values that are within a range defined by its lower and upper bounds (if any). +For example, a pattern `'m'..='p'` will match only the values `'m'`, `'n'`, `'o'`, and `'p'`. A +pattern `1..` will match values equal to or greater than 1, but not 0 (or negative numbers, for +signed integers). The bounds can be literals or paths that point to constant values. A pattern a `..=` b must always have a ≤ b. It is an error to have a range pattern `10..=0`, for example. From abcacb2ce47e163e16ba1d7ec8d020ad6629a8d3 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 25 Feb 2021 13:43:17 -0800 Subject: [PATCH 02/32] More formal writing and links --- src/patterns.md | 15 ++++++++++----- src/tokens.md | 3 ++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/patterns.md b/src/patterns.md index 763633c858..35f2b030e9 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -402,7 +402,6 @@ match tuple { > **Syntax**\ > _RangePattern_ :\ >    _RangePatternBound_ `..=` _RangePatternBound_ -> _RangeFromPattern_ :\ >    _RangePatternBound_ `..` > > _ObsoleteRangePattern_ :\ @@ -416,10 +415,16 @@ match tuple { >    | [_PathInExpression_]\ >    | [_QualifiedPathInExpression_] -Range patterns match values that are within a range defined by its lower and upper bounds (if any). -For example, a pattern `'m'..='p'` will match only the values `'m'`, `'n'`, `'o'`, and `'p'`. A -pattern `1..` will match values equal to or greater than 1, but not 0 (or negative numbers, for -signed integers). The bounds can be literals or paths that point to constant values. +Range patterns match values within the range defined by their bounds. A range pattern may be +closed or half-open. A range pattern is closed if it has both a lower and an upper bound, and +it matches all the values between and including both of its bounds. A range pattern that is +half-open is written with a lower bound but not an upper bound, and matches any value equal to +or greater than the specified lower bound. + +For example, a pattern `'m'..='p'` will match only the values `'m'`, `'n'`, `'o'`, and `'p'`. The +pattern `1..` will match 9, or 9001, or 9007199254740991 (if it is of an appropriate size), but +not 0 or negative numbers for signed integers. The bounds can be literals or paths that point +to constant values. A pattern a `..=` b must always have a ≤ b. It is an error to have a range pattern `10..=0`, for example. diff --git a/src/tokens.md b/src/tokens.md index f329ce9124..ba431303e2 100644 --- a/src/tokens.md +++ b/src/tokens.md @@ -586,7 +586,7 @@ usages and meanings are defined in the linked pages. | `@` | At | [Subpattern binding] | `_` | Underscore | [Wildcard patterns], [Inferred types], Unnamed items in [constants], [extern crates], and [use declarations] | `.` | Dot | [Field access][field], [Tuple index] -| `..` | DotDot | [Range][range], [Struct expressions], [Patterns] +| `..` | DotDot | [Range][range], [Struct expressions], [Patterns], [Range Patterns][rangepat] | `...` | DotDotDot | [Variadic functions][extern], [Range patterns] | `..=` | DotDotEq | [Inclusive Range][range], [Range patterns] | `,` | Comma | Various separators @@ -646,6 +646,7 @@ them are referred to as "token trees" in [macros]. The three types of brackets [patterns]: patterns.md [question]: expressions/operator-expr.md#the-question-mark-operator [range]: expressions/range-expr.md +[rangepat]: patterns.md#range-patterns [raw pointers]: types/pointer.md#raw-pointers-const-and-mut [references]: types/pointer.md [sized]: trait-bounds.md#sized From 51fab18a424ca774bf1c8f352db7caed18e75a03 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 6 Apr 2021 15:32:53 -0700 Subject: [PATCH 03/32] Offer example --- src/patterns.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/patterns.md b/src/patterns.md index 35f2b030e9..d00c270e9d 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -458,6 +458,12 @@ println!("{}", match ph { _ => unreachable!(), }); +# let uint: u32 = 5; +match uint { + 0 => "zero!", + 1.. => "positive number!", +}; + // using paths to constants: # const TROPOSPHERE_MIN : u8 = 6; # const TROPOSPHERE_MAX : u8 = 20; From 91c95a18e6aa0f5f687bab55a43fa0290afb6f1c Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 9 May 2021 20:26:02 -0700 Subject: [PATCH 04/32] Expand on Unicode identifiers. --- src/identifiers.md | 62 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/src/identifiers.md b/src/identifiers.md index e57be8407a..c6361c4db9 100644 --- a/src/identifiers.md +++ b/src/identifiers.md @@ -2,8 +2,8 @@ > **Lexer:**\ > IDENTIFIER_OR_KEYWORD :\ ->       XID_start XID_continue\*\ ->    | `_` XID_continue+ +>       XID_Start XID_Continue\*\ +>    | `_` XID_Continue+ > > RAW_IDENTIFIER : `r#` IDENTIFIER_OR_KEYWORD *Except `crate`, `self`, `super`, `Self`* > @@ -12,29 +12,59 @@ > IDENTIFIER :\ > NON_KEYWORD_IDENTIFIER | RAW_IDENTIFIER -An identifier is any nonempty Unicode string of the following form: + +Identifiers follow the specification in [Unicode Standard Annex #31][UAX31] for Unicode version 13.0, with the additions described below. Some examples of identifiers: -Either +* `foo` +* `_identifier` +* `r#true` +* `Москва` +* `東京` -* The first character has property [`XID_start`]. -* The remaining characters have property [`XID_continue`]. +The profile used from UAX #31 is: -Or +* Start := [`XID_Start`], plus the underscore character (U+005F) +* Continue := [`XID_Continue`] +* Medial := empty -* The first character is `_`. -* The identifier is more than one character. `_` alone is not an identifier. -* The remaining characters have property [`XID_continue`]. +> **Note**: Identifiers starting with an underscore are typically used to indicate an identifier that is intentionally unused, and will silence the unused warning in `rustc`. -> **Note**: [`XID_start`] and [`XID_continue`] as character properties cover the -> character ranges used to form the more familiar C and Java language-family -> identifiers. +Identifiers may not be a [strict] or [reserved] keyword without the `r#` prefix described below in [raw identifiers](#raw-identifiers). + +Zero width non-joiner (ZWNJ U+200C) and zero width joiner (ZWJ U+200D) characters are not allowed in identifiers. + +Identifiers are restricted to the ASCII subset of [`XID_Start`] and [`XID_Continue`] in the following situations: + +* [`extern crate`] declarations +* External crate names referenced in a [path] +* [Module] names loaded from the filesystem without a [`path` attribute] +* [`no_mangle`] attributed items +* Item names in [external blocks] + +## Normalization + +Identifiers are normalized using Normalization Form C (NFC) as defined in [Unicode Standard Annex #15][UAX15]. Two identifiers are equal if their NFC forms are equal. + +[Procedural][proc-macro] and [declarative][mbe] macros receive normalized identifiers in their input. + +## Raw identifiers A raw identifier is like a normal identifier, but prefixed by `r#`. (Note that the `r#` prefix is not included as part of the actual identifier.) Unlike a normal identifier, a raw identifier may be any strict or reserved keyword except the ones listed above for `RAW_IDENTIFIER`. -[strict]: keywords.md#strict-keywords +[`extern crate`]: items/extern-crates.md +[`no_mangle`]: abi.md#the-no_mangle-attribute +[`path` attribute]: items/modules.md#the-path-attribute +[`XID_Continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i= +[`XID_Start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i= +[external blocks]: items/external-blocks.md +[mbe]: macros-by-example.md +[module]: items/modules.md +[path]: paths.md +[proc-macro]: procedural-macros.md [reserved]: keywords.md#reserved-keywords -[`XID_start`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i= -[`XID_continue`]: http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i= +[strict]: keywords.md#strict-keywords +[UAX15]: https://www.unicode.org/reports/tr15/tr15-50.html +[UAX31]: https://www.unicode.org/reports/tr31/tr31-33.html From 65562046464c62814f1c1052584d9fd7f27c5008 Mon Sep 17 00:00:00 2001 From: marcusdunn Date: Sun, 16 May 2021 20:22:18 -0700 Subject: [PATCH 05/32] added an example for binding after @ --- src/patterns.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/patterns.md b/src/patterns.md index 7676d5255b..7ec9a4e531 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -384,6 +384,9 @@ match slice { // `end` is a slice of everything but the first element, which must be "a". ["a", end @ ..] => println!("ends with: {:?}", end), + // 'whole' is the entire slice and `last` the the final element + whole @ [.., last] => println!("the last element of {:?} is {}", whole, last) + rest => println!("{:?}", rest), } From 0ca365f49371b615d1669d648aff4533d343b1df Mon Sep 17 00:00:00 2001 From: marcusdunn Date: Sun, 16 May 2021 20:25:01 -0700 Subject: [PATCH 06/32] removed repeated word --- src/patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/patterns.md b/src/patterns.md index 7ec9a4e531..16cfac870d 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -384,7 +384,7 @@ match slice { // `end` is a slice of everything but the first element, which must be "a". ["a", end @ ..] => println!("ends with: {:?}", end), - // 'whole' is the entire slice and `last` the the final element + // 'whole' is the entire slice and `last` is the final element whole @ [.., last] => println!("the last element of {:?} is {}", whole, last) rest => println!("{:?}", rest), From 702bad4479c1a79fc386bde916a5a21deafd75d8 Mon Sep 17 00:00:00 2001 From: marcusdunn Date: Sun, 16 May 2021 20:33:33 -0700 Subject: [PATCH 07/32] added a comma --- src/patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/patterns.md b/src/patterns.md index 16cfac870d..43fde6e7a8 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -385,7 +385,7 @@ match slice { ["a", end @ ..] => println!("ends with: {:?}", end), // 'whole' is the entire slice and `last` is the final element - whole @ [.., last] => println!("the last element of {:?} is {}", whole, last) + whole @ [.., last] => println!("the last element of {:?} is {}", whole, last), rest => println!("{:?}", rest), } From 80f4867bcc2baaa9bcdb650c43276cd98883389c Mon Sep 17 00:00:00 2001 From: "Carol (Nichols || Goulding)" <193874+carols10cents@users.noreply.github.com> Date: Tue, 20 Jul 2021 20:45:09 -0400 Subject: [PATCH 08/32] Remove incorrect apostrophe This should be the other form of its. --- src/types/tuple.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/tuple.md b/src/types/tuple.md index 3877a2672f..df21e1cdf4 100644 --- a/src/types/tuple.md +++ b/src/types/tuple.md @@ -22,7 +22,7 @@ And so on. The type of each field is the type of the same position in the tuple's list of types. For convenience and historical reasons, the tuple type with no fields (`()`) is often called *unit* or *the unit type*. -It's one value is also called *unit* or *the unit value*. +Its one value is also called *unit* or *the unit value*. Some examples of tuple types: From 83f725f1b9dda6166589d7b715b75b7f54143b8e Mon Sep 17 00:00:00 2001 From: kklibo Date: Thu, 3 Jun 2021 15:51:55 -0700 Subject: [PATCH 09/32] minor grammar and typo fixes --- src/destructors.md | 5 +++-- src/dynamically-sized-types.md | 2 +- src/expressions.md | 4 ++-- src/expressions/method-call-expr.md | 2 +- src/glossary.md | 2 +- src/items/extern-crates.md | 2 +- src/items/external-blocks.md | 2 +- src/items/functions.md | 2 +- src/items/implementations.md | 6 +++--- src/items/modules.md | 2 +- src/lifetime-elision.md | 2 +- src/names/preludes.md | 2 +- 12 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 82ca73cddc..4b8e4ffcab 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -154,7 +154,7 @@ temporary variable that holds the result of that expression when used in a [place context], unless it is [promoted]. Apart from lifetime extension, the temporary scope of an expression is the -smallest scope that contains the expression and is for one of the following: +smallest scope that contains the expression and is one of the following: * The entire function body. * A statement. @@ -246,7 +246,8 @@ loop { ### Constant promotion Promotion of a value expression to a `'static` slot occurs when the expression -could be written in a constant, borrowed, and dereferencing that borrow where +could be written in a constant and borrowed, and that borrow could be dereferenced +where the expression was originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain [interior mutability] or [destructors] (these diff --git a/src/dynamically-sized-types.md b/src/dynamically-sized-types.md index 1a5fae7270..cab1ec510e 100644 --- a/src/dynamically-sized-types.md +++ b/src/dynamically-sized-types.md @@ -18,7 +18,7 @@ types">DSTs. Such types can only be used in certain cases: types">DSTs. Unlike with generic type parameters, `Self: ?Sized` is the default in trait definitions. * Structs may contain a DST as the - last field, this makes the struct itself a + last field; this makes the struct itself a DST. > **Note**: [variables], function parameters, [const] items, and [static] items must be diff --git a/src/expressions.md b/src/expressions.md index 35e7916149..b0b7e8f8ce 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -179,11 +179,11 @@ move the value. Only the following place expressions may be moved out of: * [Variables] which are not currently borrowed. * [Temporary values](#temporaries). * [Fields][field] of a place expression which can be moved out of and - doesn't implement [`Drop`]. + don't implement [`Drop`]. * The result of [dereferencing][deref] an expression with type [`Box`] and that can also be moved out of. -When moving out of a place expression that evaluates to a local variable, the +After moving out of a place expression that evaluates to a local variable, the location is deinitialized and cannot be read from again until it is reinitialized. In all other cases, trying to use a place expression in a value expression context is an error. diff --git a/src/expressions/method-call-expr.md b/src/expressions/method-call-expr.md index 5e7caa5e47..748e7dc1e4 100644 --- a/src/expressions/method-call-expr.md +++ b/src/expressions/method-call-expr.md @@ -71,7 +71,7 @@ These cases require a [disambiguating function call syntax] for method and funct ***Warning:*** For [trait objects], if there is an inherent method of the same name as a trait method, it will give a compiler error when trying to call the method in a method call expression. Instead, you can call the method using [disambiguating function call syntax], in which case it calls the trait method, not the inherent method. There is no way to call the inherent method. -Just don't define inherent methods on trait objects with the same name a trait method and you'll be fine. +Just don't define inherent methods on trait objects with the same name as a trait method and you'll be fine. diff --git a/src/glossary.md b/src/glossary.md index e345634a84..8a3a270a18 100644 --- a/src/glossary.md +++ b/src/glossary.md @@ -125,7 +125,7 @@ implementation. A variable is initialized if it has been assigned a value and hasn't since been moved from. All other memory locations are assumed to be uninitialized. Only -unsafe Rust can create such a memory without initializing it. +unsafe Rust can create a memory location without initializing it. ### Local trait diff --git a/src/items/extern-crates.md b/src/items/extern-crates.md index 7c2f9ad762..f4dc735b09 100644 --- a/src/items/extern-crates.md +++ b/src/items/extern-crates.md @@ -64,7 +64,7 @@ by using an underscore with the form `extern crate foo as _`. This may be useful for crates that only need to be linked, but are never referenced, and will avoid being reported as unused. -The [`macro_use` attribute] works as usual and import the macro names +The [`macro_use` attribute] works as usual and imports the macro names into the [`macro_use` prelude]. ## The `no_link` attribute diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index 9443276d4b..9ad100fbd5 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -17,7 +17,7 @@ External blocks provide _declarations_ of items that are not _defined_ in the current crate and are the basis of Rust's foreign function interface. These are akin to unchecked imports. -Two kind of item _declarations_ are allowed in external blocks: [functions] and +Two kinds of item _declarations_ are allowed in external blocks: [functions] and [statics]. Calling functions or accessing statics that are declared in external blocks is only allowed in an `unsafe` context. diff --git a/src/items/functions.md b/src/items/functions.md index f882a24634..183561c921 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -74,7 +74,7 @@ If the first parameter is a _SelfParam_, this indicates that the function is a function] in a [trait] or [implementation]. A parameter with the `...` token indicates a [variadic function], and may only -be used as the last parameter of a [external block] function. The variadic +be used as the last parameter of an [external block] function. The variadic parameter may have an optional identifier, such as `args: ...`. ## Function body diff --git a/src/items/implementations.md b/src/items/implementations.md index cb3598e8e6..ee651cee54 100644 --- a/src/items/implementations.md +++ b/src/items/implementations.md @@ -87,8 +87,8 @@ fn main() { ## Trait Implementations A _trait implementation_ is defined like an inherent implementation except that -the optional generic type declarations is followed by a [trait] followed -by the keyword `for`. Followed by a path to a nominal type. +the optional generic type declarations are followed by a [trait], followed +by the keyword `for`, followed by a path to a nominal type. @@ -265,7 +265,7 @@ impl<'a> HasAssocType for Struct { Implementations may contain outer [attributes] before the `impl` keyword and inner [attributes] inside the brackets that contain the associated items. Inner -attributes must come before any associated items. That attributes that have +attributes must come before any associated items. The attributes that have meaning here are [`cfg`], [`deprecated`], [`doc`], and [the lint check attributes]. diff --git a/src/items/modules.md b/src/items/modules.md index ff9cae078c..2a0ad55c58 100644 --- a/src/items/modules.md +++ b/src/items/modules.md @@ -65,7 +65,7 @@ contents in a file named `mod.rs` within that directory. The above example can alternately be expressed with `crate::util`'s contents in a file named `util/mod.rs`. It is not allowed to have both `util.rs` and `util/mod.rs`. -> **Note**: Previous to `rustc` 1.30, using `mod.rs` files was the way to load +> **Note**: Prior to `rustc` 1.30, using `mod.rs` files was the way to load > a module with nested children. It is encouraged to use the new naming > convention as it is more consistent, and avoids having many files named > `mod.rs` within a project. diff --git a/src/lifetime-elision.md b/src/lifetime-elision.md index 201e34410c..e8a20e2e82 100644 --- a/src/lifetime-elision.md +++ b/src/lifetime-elision.md @@ -103,7 +103,7 @@ If neither of those rules apply, then the bounds on the trait are used: // For the following trait... trait Foo { } -// These two are the same as Box has no lifetime bound on T +// These two are the same because Box has no lifetime bound on T type T1 = Box; type T2 = Box; diff --git a/src/names/preludes.md b/src/names/preludes.md index 6027763157..4c1568ada6 100644 --- a/src/names/preludes.md +++ b/src/names/preludes.md @@ -3,7 +3,7 @@ A *prelude* is a collection of names that are automatically brought into scope of every module in a crate. -These prelude names are not part of the module itself, they are implicitly +These prelude names are not part of the module itself: they are implicitly queried during [name resolution]. For example, even though something like [`Box`] is in scope in every module, you cannot refer to it as `self::Box` because it is not a member of the current module. From 91b36e4ca98dbb8a465a74fc3766d8643c84a25d Mon Sep 17 00:00:00 2001 From: kklibo Date: Thu, 3 Jun 2021 16:01:39 -0700 Subject: [PATCH 10/32] improve for loop description (copied from 'while let' equivalent) --- src/expressions/loop-expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions/loop-expr.md b/src/expressions/loop-expr.md index 1ca62095c4..308f3e3466 100644 --- a/src/expressions/loop-expr.md +++ b/src/expressions/loop-expr.md @@ -148,7 +148,7 @@ for n in 1..11 { assert_eq!(sum, 55); ``` -A for loop is equivalent to the following block expression. +A `for` loop is equivalent to a `loop` expression containing a [`match` expression] as follows: ```rust,ignore From 5ae4a38e36135e02e01933ba81d206b1c2e9ce70 Mon Sep 17 00:00:00 2001 From: kklibo Date: Thu, 3 Jun 2021 16:13:46 -0700 Subject: [PATCH 11/32] clean up grammar in 'slice types' --- src/types/slice.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/types/slice.md b/src/types/slice.md index e780277642..6ba5e7d211 100644 --- a/src/types/slice.md +++ b/src/types/slice.md @@ -7,13 +7,12 @@ A slice is a [dynamically sized type] representing a 'view' into a sequence of elements of type `T`. The slice type is written as `[T]`. -To use a slice type it generally has to be used behind a pointer for example -as: +Slice types are generally used through pointer types. For example: -* `&[T]`, a 'shared slice', often just called a 'slice', it doesn't own the - data it points to, it borrows it. -* `&mut [T]`, a 'mutable slice', mutably borrows the data it points to. -* `Box<[T]>`, a 'boxed slice' +* `&[T]`: a 'shared slice', often just called a 'slice'. It doesn't own the + data it points to; it borrows it. +* `&mut [T]`: a 'mutable slice'. It mutably borrows the data it points to. +* `Box<[T]>`: a 'boxed slice' Examples: From e4833e4076305e4554baf480f7443eb82f09a28c Mon Sep 17 00:00:00 2001 From: kklibo Date: Thu, 3 Jun 2021 16:19:04 -0700 Subject: [PATCH 12/32] disambiguate sentence The phrase 'may not' could mean 'might not' or 'is not allowed to'. --- src/types/closure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/closure.md b/src/types/closure.md index 7cfb3b059f..eecdb038f1 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -106,7 +106,7 @@ let z = &x; In this case, borrowing `x` mutably is not possible, because `x` is not `mut`. But at the same time, borrowing `x` immutably would make the assignment illegal, -because a `& &mut` reference may not be unique, so it cannot safely be used to +because a `& &mut` reference might not be unique, so it cannot safely be used to modify a value. So a unique immutable borrow is used: it borrows `x` immutably, but like a mutable borrow, it must be unique. In the above example, uncommenting the declaration of `y` will produce an error because it would violate the From 44bac0ebada9ce08ab5e8a25c9815befe7075a90 Mon Sep 17 00:00:00 2001 From: kklibo Date: Thu, 3 Jun 2021 16:34:58 -0700 Subject: [PATCH 13/32] disambiguate sentence The original sentence could mean that "its destructor is run" and "it is dropped" are two separate possibilities, rather than two names for the same action. --- src/destructors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/destructors.md b/src/destructors.md index 4b8e4ffcab..fed40d4674 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -1,7 +1,7 @@ # Destructors When an [initialized] [variable] or [temporary] goes out of -[scope](#drop-scopes) its *destructor* is run, or it is *dropped*. [Assignment] +[scope](#drop-scopes), its *destructor* is run (also phrased as: "it is *dropped*"). [Assignment] also runs the destructor of its left-hand operand, if it's initialized. If a variable has been partially initialized, only its initialized fields are dropped. From 314567f7cdfde14f4f615eac971ec7f25b5e85b3 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 26 Jul 2021 13:10:37 -0700 Subject: [PATCH 14/32] Revert phrase change for destructors. It is not clear if the original intended to mean these as two separate concepts, or synonyms for the same thing. --- src/destructors.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/destructors.md b/src/destructors.md index fed40d4674..cf7ab0aca2 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -1,7 +1,7 @@ # Destructors When an [initialized] [variable] or [temporary] goes out of -[scope](#drop-scopes), its *destructor* is run (also phrased as: "it is *dropped*"). [Assignment] +[scope](#drop-scopes), its *destructor* is run, or it is *dropped*. [Assignment] also runs the destructor of its left-hand operand, if it's initialized. If a variable has been partially initialized, only its initialized fields are dropped. From ae773c1a19d6ebe3922b9739b5dbf8b762fd327c Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Wed, 28 Jul 2021 04:32:23 +0900 Subject: [PATCH 15/32] Mention "function item type to `fn pointer`" coercion --- src/type-coercions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/type-coercions.md b/src/type-coercions.md index 2b48a0645e..db143cf4cb 100644 --- a/src/type-coercions.md +++ b/src/type-coercions.md @@ -150,6 +150,8 @@ Coercion is allowed between the following types: structs. In addition, coercions from sub-traits to super-traits will be added. See [RFC 401] for more details.--> +* Function item types to `fn` pointers + * Non capturing closures to `fn` pointers * `!` to any `T` From 9dd6a1f1194946e0d5eb2c76525cbbfc187a6f6a Mon Sep 17 00:00:00 2001 From: DaErich Date: Wed, 28 Jul 2021 22:12:33 +0200 Subject: [PATCH 16/32] array-expr.md: Fix typo;'polished' sentence --- src/expressions/array-expr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions/array-expr.md b/src/expressions/array-expr.md index 65b3e24917..432a45ad80 100644 --- a/src/expressions/array-expr.md +++ b/src/expressions/array-expr.md @@ -21,7 +21,7 @@ The syntax for the second form is two expressions separated by a semicolon (`;`) The expression before the `;` is called the *repeat operand*. The expression after the `;` is called the *length operand*. It must have type `usize` and be a [constant expression], such as a [literal] or a [constant item]. -An array expression of this form creates an array with the length of the value of the legnth operand with each element a copy of the repeat operand. +An array expression of this form creates an array with the length of the value of the length operand with each element being a copy of the repeat operand. That is, `[a; b]` creates an array containing `b` copies of the value of `a`. If the length operand has a value greater than 1 then this requires that the type of the repeat operand is [`Copy`] or that it must be a [path] to a constant item. From da75d920c5db7462c38cfb58e5dce7e9b9b3cec9 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Tue, 3 Aug 2021 00:29:26 -0700 Subject: [PATCH 17/32] add spacing in src/patterns.md Co-authored-by: Eric Huss --- src/patterns.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/patterns.md b/src/patterns.md index d00c270e9d..0a0af6707d 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -401,8 +401,8 @@ match tuple { > **Syntax**\ > _RangePattern_ :\ ->    _RangePatternBound_ `..=` _RangePatternBound_ ->    _RangePatternBound_ `..` +>       _RangePatternBound_ `..=` _RangePatternBound_\ +>    | _RangePatternBound_ `..` > > _ObsoleteRangePattern_ :\ >    _RangePatternBound_ `...` _RangePatternBound_ From 7d5f0cdc4d3eb7d6866a4cea0dbb793479c19ff0 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 19 Aug 2021 07:39:49 +0000 Subject: [PATCH 18/32] Allow users to change status labels --- triagebot.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index fc4b19e20f..4059f91906 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,6 +1,6 @@ [relabel] allow-unauthenticated = [ - "A-*", "New Content", "Language Cleanup", "Easy", "Formatting", "Enhancement", "Bug", + "S-*", "A-*", "New Content", "Language Cleanup", "Easy", "Formatting", "Enhancement", "Bug", ] [assign] From daa0b4858eec6736c27f50920bce7ab50e3ae65a Mon Sep 17 00:00:00 2001 From: DaErich Date: Sat, 28 Aug 2021 20:06:49 +0200 Subject: [PATCH 19/32] expressions.md: Attempt fixing broken grammar in Mutability paragraph --- src/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expressions.md b/src/expressions.md index b0b7e8f8ce..88f7fbac96 100644 --- a/src/expressions.md +++ b/src/expressions.md @@ -206,7 +206,7 @@ The following expressions can be mutable place expression contexts: * Dereference of a variable, or field of a variable, with type `&mut T`. Note: This is an exception to the requirement of the next rule. * Dereferences of a type that implements `DerefMut`: this then requires that - the value being dereferenced is evaluated is a mutable place expression context. + the value being dereferenced is evaluated in a mutable place expression context. * [Array indexing] of a type that implements `IndexMut`: this then evaluates the value being indexed, but not the index, in mutable place expression context. From 064e68d9f878d6e98e12776562fa9306ef851f10 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 3 Sep 2021 18:13:14 -0700 Subject: [PATCH 20/32] Fix recursion_limit example. --- src/attributes/limits.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/attributes/limits.md b/src/attributes/limits.md index 517637e5dc..65cb55b43a 100644 --- a/src/attributes/limits.md +++ b/src/attributes/limits.md @@ -15,10 +15,10 @@ syntax to specify the recursion depth. #![recursion_limit = "4"] macro_rules! a { - () => { a!(1) }; - (1) => { a!(2) }; - (2) => { a!(3) }; - (3) => { a!(4) }; + () => { a!(1); }; + (1) => { a!(2); }; + (2) => { a!(3); }; + (3) => { a!(4); }; (4) => { }; } From 76ed5db1e1783e8ab2642e6c69f596381126052d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Sun, 12 Sep 2021 13:52:01 -0700 Subject: [PATCH 21/32] Update with slice pattern restrictions --- src/patterns.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/patterns.md b/src/patterns.md index 0a0af6707d..3c7b324b4e 100644 --- a/src/patterns.md +++ b/src/patterns.md @@ -10,7 +10,6 @@ >    | [_IdentifierPattern_]\ >    | [_WildcardPattern_]\ >    | [_RestPattern_]\ ->    | [_ObsoleteRangePattern_]\ >    | [_ReferencePattern_]\ >    | [_StructPattern_]\ >    | [_TupleStructPattern_]\ @@ -401,7 +400,14 @@ match tuple { > **Syntax**\ > _RangePattern_ :\ ->       _RangePatternBound_ `..=` _RangePatternBound_\ +>       _InclusiveRangePattern_\ +>    | _HalfOpenRangePattern_\ +>    | _ObsoleteRangePattern_ +> +> _InclusiveRangePattern_ :\ +>       _RangePatternBound_ `..=` _RangePatternBound_ +> +> _HalfOpenRangePattern_ :\ >    | _RangePatternBound_ `..` > > _ObsoleteRangePattern_ :\ @@ -421,12 +427,14 @@ it matches all the values between and including both of its bounds. A range patt half-open is written with a lower bound but not an upper bound, and matches any value equal to or greater than the specified lower bound. -For example, a pattern `'m'..='p'` will match only the values `'m'`, `'n'`, `'o'`, and `'p'`. The +For example, a pattern `'m'..='p'` will match only the values `'m'`, `'n'`, `'o'`, and `'p'`. For an integer the pattern `1..` will match 9, or 9001, or 9007199254740991 (if it is of an appropriate size), but -not 0 or negative numbers for signed integers. The bounds can be literals or paths that point +not 0, and not negative numbers for signed integers. The bounds can be literals or paths that point to constant values. -A pattern a `..=` b must always have a ≤ b. It is an error to have a range pattern +A half-open range pattern in the style `a..` cannot be used to match within the context of a slice. + +A pattern `a..=b` must always have a ≤ b. It is an error to have a range pattern `10..=0`, for example. The `...` syntax is kept for backwards compatibility. @@ -734,6 +742,10 @@ is irrefutable. When matching a slice, it is irrefutable only in the form with a single `..` [rest pattern](#rest-patterns) or [identifier pattern](#identifier-patterns) with the `..` rest pattern as a subpattern. +Within a slice, a half-open range pattern like `a..` must be enclosed in parentheses, +as in `(a..)`, to clarify it is intended to match a single value. +A future version of Rust may give the non-parenthesized version an alternate meaning. + ## Path patterns > **Syntax**\ From 979707736cc937200ded3afae52bf8aa7501ebf2 Mon Sep 17 00:00:00 2001 From: Lazaro Hurtado Date: Wed, 29 Sep 2021 21:57:57 -0400 Subject: [PATCH 22/32] fixed link typo --- src/items/generics.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/items/generics.md b/src/items/generics.md index e9c11ffcfa..0a9703335b 100644 --- a/src/items/generics.md +++ b/src/items/generics.md @@ -37,7 +37,7 @@ Generic parameters are in scope within the item definition where they are declared. They are not in scope for items declared within the body of a function as described in [item declarations]. -[References], [raw pointers], [arrays], [slices][arrays], [tuples], and +[References], [raw pointers], [arrays], [slices], [tuples], and [function pointers] have lifetime or type parameters as well, but are not referred to with path syntax. @@ -274,6 +274,7 @@ struct Foo<#[my_flexible_clone(unbounded)] H> { [array repeat expression]: ../expressions/array-expr.md [arrays]: ../types/array.md +[slices]: ../types/slice.md [associated const]: associated-items.md#associated-constants [associated type]: associated-items.md#associated-types [block]: ../expressions/block-expr.md From 8aa82fcffa5173e0198ccf942b1435ad43c7dd9f Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 2 Oct 2021 09:05:16 +0700 Subject: [PATCH 23/32] Use subtrait/supertrait, not sub-trait, super-trait. There are definitions of subtrait and supertrait, so use the official terminology. --- src/type-coercions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/type-coercions.md b/src/type-coercions.md index db143cf4cb..45d81d3860 100644 --- a/src/type-coercions.md +++ b/src/type-coercions.md @@ -147,7 +147,7 @@ Coercion is allowed between the following types: and where `U` can be obtained from `T` by [unsized coercion](#unsized-coercions). * Function item types to `fn` pointers From 7c776c83fe66c8c74929585891931b3918ccfdd8 Mon Sep 17 00:00:00 2001 From: Roxane Date: Tue, 6 Jul 2021 12:41:44 -0400 Subject: [PATCH 24/32] Update closure types documentation so it includes information about RFC2229 --- src/types/closure.md | 301 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 254 insertions(+), 47 deletions(-) diff --git a/src/types/closure.md b/src/types/closure.md index eecdb038f1..54993e0564 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -2,38 +2,46 @@ A [closure expression] produces a closure value with a unique, anonymous type that cannot be written out. A closure type is approximately equivalent to a -struct which contains the captured variables. For instance, the following +struct which contains the captured values. For instance, the following closure: ```rust +#[derive(Debug)] +struct Point { x:i32, y:i32 } +struct Rectangle { left_top: Point, right_bottom: Point } + fn f String> (g: F) { println!("{}", g()); } -let mut s = String::from("foo"); -let t = String::from("bar"); - -f(|| { - s += &t; - s -}); -// Prints "foobar". +let mut rect = Rectangle { + left_top: Point { x: 1, y: 1 }, + right_bottom: Point { x: 0, y: 0 } +}; + +let c = || { + rect.left_top.x += 1; + rect.right_bottom.x += 1; + format!("{:?}", rect.left_top) +}; +// Prints "Point { x: 2, y: 1 }". ``` generates a closure type roughly like the following: - + ```rust,ignore struct Closure<'a> { - s : String, - t : &'a String, + left_top : &'a mut Point, + right_bottom_x : &'a mut i32, } impl<'a> FnOnce<()> for Closure<'a> { type Output = String; fn call_once(self) -> String { - self.s += &*self.t; - self.s + self.left_top.x += 1; + self.right_bottom_x += 1; + format!("{:?}", self.left_top) } } ``` @@ -42,48 +50,150 @@ so that the call to `f` works as if it were: ```rust,ignore -f(Closure{s: s, t: &t}); +f(Closure{ left_top: rect.left_top, right_bottom_x: rect.left_top.x }); ``` ## Capture modes -The compiler prefers to capture a closed-over variable by immutable borrow, +The compiler prefers to capture a value by immutable borrow, followed by unique immutable borrow (see below), by mutable borrow, and finally -by move. It will pick the first choice of these that is compatible with how the -captured variable is used inside the closure body. The compiler does not take -surrounding code into account, such as the lifetimes of involved variables, or -of the closure itself. +by move. It will pick the first choice of these that allows the closure to +compile. The choice is made only with regards to the contents of the closure +expression; the compiler does not take into account surrounding code, such as +the lifetimes of involved variables or fields. +>>>>>>> 881f305... Update closure types documentation so it includes information about RFC2229 -If the `move` keyword is used, then all captures are by move or, for `Copy` -types, by copy, regardless of whether a borrow would work. The `move` keyword is -usually used to allow the closure to outlive the captured values, such as if the -closure is being returned or used to spawn a new thread. +## Capture Precision + +The precise path that gets captured is typically the full path that is used in the closure, but there are cases where we will only capture a prefix of the path. + + +### Shared prefix -Composite types such as structs, tuples, and enums are always captured entirely, -not by individual fields. It may be necessary to borrow into a local variable in -order to capture a single field: +In the case where a path and one of the ancestor’s of that path are both captured by a closure, the ancestor path is captured with the highest capture mode among the two captures,`CaptureMode = max(AncestorCaptureMode, DescendantCaptureMode)`, using the strict weak ordering + +`ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue`. + +Note that this might need to be applied recursively. + +```rust= +let s = String::new("S"); +let t = (s, String::new("T")); +let mut u = (t, String::new("U")); + +let c = || { + println!("{:?}", u); // u captured by ImmBorrow + u.0.truncate(0); // u.0 captured by MutBorrow + move_value(u.0.0); // u.0.0 captured by ByValue +}; +``` + +Overall the closure will capture `u` by `ByValue`. + +### Wild Card Patterns +Closures only capture data that needs to be read, which means the following closures will not capture `x` ```rust -# use std::collections::HashSet; -# -struct SetVec { - set: HashSet, - vec: Vec -} +let x = 10; +let c = || { + let _ = x; +}; + +let c = || match x { + _ => println!("Hello World!") +}; +``` -impl SetVec { - fn populate(&mut self) { - let vec = &mut self.vec; - self.set.iter().for_each(|&n| { - vec.push(n); - }) - } -} +### Capturing references in move contexts + +Rust doesn't allow moving fields out of references. As a result, in the case of move closures, when values accessed through a shared references are moved into the closure body, the compiler, instead of moving the values out of the reference, would reborrow the data. + +```rust +struct T(String, String); + +let mut t = T(String::from("foo"), String::from("bar")); +let t = &mut t; +let c = move || t.0.truncate(0); // closure captures (&mut t.0) ``` -If, instead, the closure were to use `self.vec` directly, then it would attempt -to capture `self` by mutable reference. But since `self.set` is already -borrowed to iterate over, the code would not compile. +### Raw pointer dereference +In Rust, it's `unsafe` to dereference a raw pointer. Therefore, closures will only capture the prefix of a path that runs up to, but not including, the first dereference of a raw pointer. + +```rust, +struct T(String, String); + +let t = T(String::from("foo"), String::from("bar")); +let t = &t as *const T; + +let c = || unsafe { + println!("{}", (*t).0); // closure captures t +}; +``` + +### Reference into unaligned `struct`s + +In Rust, it's `unsafe` to hold references to unaligned fields in a structure, and therefore, closures will only capture the prefix of the path that runs up to, but not including, the first field access into an unaligned structure. + +```rust +#[repr(packed)] +struct T(String, String); + +let t = T(String::from("foo"), String::from("bar")); +let c = || unsafe { + println!("{}", t.0); // closure captures t +}; +``` + + +### `Box` vs other `Deref` implementations + +The compiler treats the implementation of the Deref trait for `Box` differently, as it is considered a special entity. + +For example, let us look at examples involving `Rc` and `Box`. The `*rc` is desugared to a call to the trait method `deref` defined on `Rc`, but since `*box` is treated differently by the compiler, the compiler is able to do precise capture on contents of the `Box`. + +#### Non `move` closure + +In a non `move` closure, if the contents of the `Box` are not moved into the closure body, the contents of the `Box` are precisely captured. + +```rust +# use std::rc::Rc; + +struct S(i32); + +let b = Box::new(S(10)); +let c_box = || { + println!("{}", (*b).0); // closure captures `(*b).0` +}; + +let r = Rc::new(S(10)); +let c_rc = || { + println!("{}", (*r).0); // closure caprures `r` +}; +``` + +However, if the contents of the `Box` are moved into the closure, then the box is entirely captured. This is done so the amount of data that needs to be moved into the closure is minimized. + +```rust +struct S(i32); + +let b = Box::new(S(10)); +let c_box = || { + let x = (*b).0; // closure captures `b` +}; +``` + +#### `move` closure + +Similarly to moving contents of a `Box` in a non-`move` closure, reading the contents of a `Box` in a `move` closure will capture the `Box` entirely. + +```rust +struct S(i32); + +let b = Box::new(S(10)); +let c_box = || { + println!("{}", (*b).0); // closure captures `b` +}; +``` ## Unique immutable borrows in captures @@ -113,6 +223,7 @@ the declaration of `y` will produce an error because it would violate the uniqueness of the closure's borrow of `x`; the declaration of z is valid because the closure's lifetime has expired at the end of the block, releasing the borrow. + ## Call traits and coercions Closure types all implement [`FnOnce`], indicating that they can be called once @@ -156,12 +267,13 @@ following traits if allowed to do so by the types of the captures it stores: The rules for [`Send`] and [`Sync`] match those for normal struct types, while [`Clone`] and [`Copy`] behave as if [derived]. For [`Clone`], the order of -cloning of the captured variables is left unspecified. +cloning of the captured values is left unspecified. + Because captures are often by reference, the following general rules arise: -* A closure is [`Sync`] if all captured variables are [`Sync`]. -* A closure is [`Send`] if all variables captured by non-unique immutable +* A closure is [`Sync`] if all captured values are [`Sync`]. +* A closure is [`Send`] if all values captured by non-unique immutable reference are [`Sync`], and all values captured by unique immutable or mutable reference, copy, or move are [`Send`]. * A closure is [`Clone`] or [`Copy`] if it does not capture any values by @@ -178,3 +290,98 @@ Because captures are often by reference, the following general rules arise: [`Sync`]: ../special-types-and-traits.md#sync [closure expression]: ../expressions/closure-expr.md [derived]: ../attributes/derive.md + +## Drop Order + +If a closure captures a field of a composite types such as structs, tuples, and enums by value, the field's lifetime would now be tied to the closure. As a result, it is possible for disjoint fields of a composite types to be dropped at different times. + +```rust +{ + let tuple = + (String::from("foo"), String::from("bar")); // --+ + { // | + let c = || { // ----------------------------+ | + // tuple.0 is captured into the closure | | + drop(tuple.0); // | | + }; // | | + } // 'c' and 'tuple.0' dropped here ------------+ | +} // tuple.1 dropped here -----------------------------+ +``` + +# Edition 2018 and before + +## Closure types difference + +In Edition 2018 and before, a closure would capture variables in its entirety. This means that for the example used in the [Closure types](#closure-types) section, the generated closure type would instead look something like this: + + +```rust,ignore +struct Closure<'a> { + rect : &'a mut Rectangle, +} + +impl<'a> FnOnce<()> for Closure<'a> { + type Output = String; + fn call_once(self) -> String { + self.rect.left_top.x += 1; + self.rect.right_bottom.x += 1; + format!("{:?}", self.rect.left_top) + } +} +``` +and the call to `f` would work as follows: + +```rust,ignore +f(Closure { rect: rect }); +``` + +## Capture precision difference + +Composite types such as structs, tuples, and enums are always captured in its intirety, +not by individual fields. As a result, it may be necessary to borrow into a local variable in order to capture a single field: + +```rust +# use std::collections::HashSet; +# +struct SetVec { + set: HashSet, + vec: Vec +} + +impl SetVec { + fn populate(&mut self) { + let vec = &mut self.vec; + self.set.iter().for_each(|&n| { + vec.push(n); + }) + } +} +``` + +If, instead, the closure were to use `self.vec` directly, then it would attempt +to capture `self` by mutable reference. But since `self.set` is already +borrowed to iterate over, the code would not compile. + +If the `move` keyword is used, then all captures are by move or, for `Copy` +types, by copy, regardless of whether a borrow would work. The `move` keyword is +usually used to allow the closure to outlive the captured values, such as if the +closure is being returned or used to spawn a new thread. + +Regardless of if the data will be read by the closure, i.e. in case of wild card patterns, if a variable defined outside the closure is mentioned within the closure the variable will be captured in its entirety. + +## Drop order difference + +As composite types are captured in their entirety, a closure which captures one of those composite types by value would drop the entire captured variable at the same time as the closure gets dropped. + +```rust +{ + let tuple = + (String::from("foo"), String::from("bar")); + { + let c = || { // --------------------------+ + // tuple is captured into the closure | + drop(tuple.0); // | + }; // | + } // 'c' and 'tuple' dropped here ------------+ +} +``` From 124ece696a7a1538ede41db0047ebb8776c0dd40 Mon Sep 17 00:00:00 2001 From: Roxane Date: Thu, 8 Jul 2021 20:10:43 -0400 Subject: [PATCH 25/32] Address comments --- src/types/closure.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/types/closure.md b/src/types/closure.md index 54993e0564..1158b242d7 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -76,14 +76,15 @@ In the case where a path and one of the ancestor’s of that path are both captu Note that this might need to be applied recursively. -```rust= -let s = String::new("S"); -let t = (s, String::new("T")); -let mut u = (t, String::new("U")); +```rust +# fn move_value(_: T){} +let s = String::from("S"); +let t = (s, String::from("T")); +let mut u = (t, String::from("U")); let c = || { println!("{:?}", u); // u captured by ImmBorrow - u.0.truncate(0); // u.0 captured by MutBorrow + u.1.truncate(0); // u.0 captured by MutBorrow move_value(u.0.0); // u.0.0 captured by ByValue }; ``` @@ -106,7 +107,7 @@ let c = || match x { ### Capturing references in move contexts -Rust doesn't allow moving fields out of references. As a result, in the case of move closures, when values accessed through a shared references are moved into the closure body, the compiler, instead of moving the values out of the reference, would reborrow the data. +Moving fields out of references is not allowed. As a result, in the case of move closures, when values accessed through a shared references are moved into the closure body, the compiler, instead of moving the values out of the reference, would reborrow the data. ```rust struct T(String, String); @@ -117,7 +118,7 @@ let c = move || t.0.truncate(0); // closure captures (&mut t.0) ``` ### Raw pointer dereference -In Rust, it's `unsafe` to dereference a raw pointer. Therefore, closures will only capture the prefix of a path that runs up to, but not including, the first dereference of a raw pointer. +Because it is `unsafe` to dereference a raw pointer, closures will only capture the prefix of a path that runs up to, but not including, the first dereference of a raw pointer. ```rust, struct T(String, String); @@ -132,7 +133,7 @@ let c = || unsafe { ### Reference into unaligned `struct`s -In Rust, it's `unsafe` to hold references to unaligned fields in a structure, and therefore, closures will only capture the prefix of the path that runs up to, but not including, the first field access into an unaligned structure. +Because it is `unsafe` to hold references to unaligned fields in a structure, closures will only capture the prefix of the path that runs up to, but not including, the first field access into an unaligned structure. ```rust #[repr(packed)] @@ -147,10 +148,13 @@ let c = || unsafe { ### `Box` vs other `Deref` implementations -The compiler treats the implementation of the Deref trait for `Box` differently, as it is considered a special entity. +The implementation of the [`Deref`] trait for [`Box`] is treated differently from other `Deref` implementations, as it is considered a special entity. For example, let us look at examples involving `Rc` and `Box`. The `*rc` is desugared to a call to the trait method `deref` defined on `Rc`, but since `*box` is treated differently by the compiler, the compiler is able to do precise capture on contents of the `Box`. +[`Box`]: ../special-types-and-traits.md#boxt +[`Deref`]: ../special-types-and-traits.md#deref-and-derefmut + #### Non `move` closure In a non `move` closure, if the contents of the `Box` are not moved into the closure body, the contents of the `Box` are precisely captured. @@ -337,7 +341,7 @@ f(Closure { rect: rect }); ## Capture precision difference -Composite types such as structs, tuples, and enums are always captured in its intirety, +Composite types such as structs, tuples, and enums are always captured in its entirety, not by individual fields. As a result, it may be necessary to borrow into a local variable in order to capture a single field: ```rust From fa56c013aa7b5e642b2c2f734eedd5580062cbc5 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 20 Jul 2021 04:10:29 -0400 Subject: [PATCH 26/32] Copy capture analyis alogrithm from hackmd --- src/types/closure.md | 133 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/src/types/closure.md b/src/types/closure.md index 1158b242d7..9159702544 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -312,6 +312,139 @@ If a closure captures a field of a composite types such as structs, tuples, and } // tuple.1 dropped here -----------------------------+ ``` + +## Overall Capture analysis algorithm + +* Input: + * Analyzing the closure C yields a set of `(Mode, Place)` pairs that are accessed + * Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max) + * Closure mode is `ref` or `move` +* Output: + * Minimal `(Mode, Place)` pairs that are actually captured +* Cleanup and truncation + * Generate C' by mapping each (Mode, Place) in C: + * `(Mode1, Place1) = ref_opt(unsafe_check(copy_type(Mode, Place)))` + * if this is a ref closure: + * Add `ref_xform(Mode1, Place1)` to C' + * else: + * Add `move_xform(Mode1, Place1)` to C' +* Minimization + * Until no rules apply: + * For each two places (M1, P1), (M2, P2) where P1 is a prefix of P2: + * Remove both places from the set + * Add (max(M1, M2), P1) into the set +* Helper functions: + * `copy_type(Mode, Place) -> (Mode, Place)` + * "By-value use of a copy type is a ref" + * If Mode = "by-value" and type(Place) is `Copy`: + * Return (ref, Place) + * Else + * Return (Mode, Place) + * `unsafe_check(Mode, Place) -> (Mode, Place)` + * "Ensure unsafe accesses occur within the closure" + * If Place contains a deref of a raw pointer: + * Let Place1 = Place truncated just before the deref + * Return (Mode, Place1) + * If Mode is `ref *` and the place contains a field of a packed struct: + * Let Place1 = Place truncated just before the field + * Return (Mode, Place1) + * Else + * Return (Mode, Place1) + * `move_xform(Mode, Place) -> (Mode, Place)` (For move closures) + * "Take ownership if data being accessed is owned by the variable used to access it (or if closure attempts to move data that it doesn't own)." + * "When taking ownership, only capture data found on the stack." + * "Otherwise, reborrow the reference." + * If Mode is `ref mut` and the place contains a deref of an `&mut`: + * Return (Mode, Place) + * Else if Mode is `ref *` and the place contains a deref of an `&`: + * Return (Mode, Place) + * Else if place contains a deref: + * Let Place1 = Place truncated just before the deref + * Return (ByValue, Place1) + * Else: + * Return (ByValue, Place) + * `ref_xform(Mode, Place) -> (Mode, Place)` (for ref closures) + * "If taking ownership of data, only move data from enclosing stack frame." + * Generate C' by mapping each (Mode, Place) in C + * If Mode is ByValue and place contains a deref: + * Let Place1 = Place truncated just before the deref + * Return (ByValue, Place1) + * Else: + * Return (Mode, Place) + * `ref_opt(Mode, Place) -> (Mode, Place)` (for ref closures) + * "Optimization: borrow the ref, not data owned by ref." + * If Place contains a deref of an `&`... + * ...or something + +## Key examples + +### box-mut + +```rust +fn box_mut() { + let mut s = Foo { x: 0 } ; + + let px = &mut s; + let bx = Box::new(px); + + + let c = #[rustc_capture_analysis] move || bx.x += 10; + // Mutable reference to this place: + // (*(*bx)).x + // ^ ^ + // | a Box + // a &mut +} +``` + +``` +Closure mode = move +C = { + (ref mut, (*(*bx)).x) +} +C' = C +``` + +Output is the same: `C' = C` + +### Packed-field-ref-and-move + +When you have a closure that both references a packed field (which is unsafe) and moves from it (which is safe) we capture the entire struct, rather than just moving the field. This is to aid in predictability, so that removing the move doesn't make the closure become unsafe: + +```rust +print(&packed.x); +move_value(packed.x); +``` + + +```rust +struct Point { x: i32, y: i32 } +fn f(p: &Point) -> impl Fn() { + let c = move || { + let x = p.x; + }; + + // x.x -> ByValue + // after rules x -> ByValue + + c +} + +struct Point { x: i32, y: i32 } +fn g(p: &mut Point) -> impl Fn() { + let c = move || { + let x = p.x; // ought to: (ref, (*p).x) + }; + + move || { + p.y += 1; + } + + + // x.x -> ByValue + +``` + # Edition 2018 and before ## Closure types difference From 52a9af968e4b23b365c6af252365925e0968b7f2 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Tue, 20 Jul 2021 04:46:00 -0400 Subject: [PATCH 27/32] Update copy type, optimization, make algorithm (place, mode) --- src/types/closure.md | 137 ++++++++++++++++++++++++++----------------- 1 file changed, 82 insertions(+), 55 deletions(-) diff --git a/src/types/closure.md b/src/types/closure.md index 9159702544..91007ec6fd 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -316,71 +316,77 @@ If a closure captures a field of a composite types such as structs, tuples, and ## Overall Capture analysis algorithm * Input: - * Analyzing the closure C yields a set of `(Mode, Place)` pairs that are accessed + * Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed * Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max) + * For a `Place` that is used in two different acess modes within the same closure, the mode reported from closure analysis is the maximum access mode. + * Note: `ByValue` use of a `Copy` type is seen as a `ref` access mode. * Closure mode is `ref` or `move` * Output: - * Minimal `(Mode, Place)` pairs that are actually captured + * Minimal `(Place, Mode)` pairs that are actually captured * Cleanup and truncation * Generate C' by mapping each (Mode, Place) in C: - * `(Mode1, Place1) = ref_opt(unsafe_check(copy_type(Mode, Place)))` + * `(Place1, Mode1) = ref_opt(unsafe_check(Place, Mode))` * if this is a ref closure: - * Add `ref_xform(Mode1, Place1)` to C' + * Add `ref_xform(Place1, Mode1)` to C' * else: - * Add `move_xform(Mode1, Place1)` to C' + * Add `move_xform(Place1, Mode1)` to C' * Minimization * Until no rules apply: - * For each two places (M1, P1), (M2, P2) where P1 is a prefix of P2: + * For each two places (P1, M1), (P2, M2) where P1 is a prefix of P2: * Remove both places from the set - * Add (max(M1, M2), P1) into the set + * Add (P1, max(M1, M2)) into the set * Helper functions: - * `copy_type(Mode, Place) -> (Mode, Place)` - * "By-value use of a copy type is a ref" - * If Mode = "by-value" and type(Place) is `Copy`: - * Return (ref, Place) - * Else - * Return (Mode, Place) - * `unsafe_check(Mode, Place) -> (Mode, Place)` + * `unsafe_check(Place, Mode) -> (Place, Mode)` * "Ensure unsafe accesses occur within the closure" * If Place contains a deref of a raw pointer: * Let Place1 = Place truncated just before the deref - * Return (Mode, Place1) + * Return (Place1, Mode) * If Mode is `ref *` and the place contains a field of a packed struct: * Let Place1 = Place truncated just before the field - * Return (Mode, Place1) + * Return (Place1, Mode) * Else - * Return (Mode, Place1) - * `move_xform(Mode, Place) -> (Mode, Place)` (For move closures) + * Return (Place, Mode) + * `move_xform(Place, Mode) -> (Place, Mode)` (For move closures) * "Take ownership if data being accessed is owned by the variable used to access it (or if closure attempts to move data that it doesn't own)." * "When taking ownership, only capture data found on the stack." * "Otherwise, reborrow the reference." * If Mode is `ref mut` and the place contains a deref of an `&mut`: - * Return (Mode, Place) + * Return (Place, Mode) * Else if Mode is `ref *` and the place contains a deref of an `&`: - * Return (Mode, Place) + * Return (Place, Mode) * Else if place contains a deref: * Let Place1 = Place truncated just before the deref - * Return (ByValue, Place1) + * Return (Place1, ByValue) * Else: - * Return (ByValue, Place) - * `ref_xform(Mode, Place) -> (Mode, Place)` (for ref closures) + * Return (Place, ByValue) + * `ref_xform(Place, Mode) -> (Place, Mode)` (for ref closures) * "If taking ownership of data, only move data from enclosing stack frame." * Generate C' by mapping each (Mode, Place) in C * If Mode is ByValue and place contains a deref: * Let Place1 = Place truncated just before the deref - * Return (ByValue, Place1) + * Return (Place1, ByValue) * Else: - * Return (Mode, Place) - * `ref_opt(Mode, Place) -> (Mode, Place)` (for ref closures) + * Return (Place, Mode) + * `ref_opt(Place, Mode) -> (Place, Mode)` * "Optimization: borrow the ref, not data owned by ref." - * If Place contains a deref of an `&`... - * ...or something + * Disjoint capture over immutable reference doesn't add too much value because the fields can either be borrowed immutably or copied. + * Edge case: Field that is accessed via the referece lives longer than the reference. + * Resolution: Only consider the last Deref + * If Place is (Base, Projections), where Projections is a list of size N. + * For all `i, 0 <= i < N`, Projections[i] != Deref + * Return (Place, Mode) + * If `l, 0 <= l < N` is the last/rightmost Deref Projection i.e. for any `i, l < i < N` Projection[i] != Deref, + and `Place.type_before_projection(l) = ty::Ref(.., Mutability::Not)` + * Let Place1 = (Base, Projections[0..=l]) + * Return (Place1, Ref) ## Key examples ### box-mut ```rust +struct Foo { x: i32 } + fn box_mut() { let mut s = Foo { x: 0 } ; @@ -388,7 +394,7 @@ fn box_mut() { let bx = Box::new(px); - let c = #[rustc_capture_analysis] move || bx.x += 10; + let c = move || bx.x += 10; // Mutable reference to this place: // (*(*bx)).x // ^ ^ @@ -397,7 +403,8 @@ fn box_mut() { } ``` -``` + +```ignore Closure mode = move C = { (ref mut, (*(*bx)).x) @@ -412,37 +419,57 @@ Output is the same: `C' = C` When you have a closure that both references a packed field (which is unsafe) and moves from it (which is safe) we capture the entire struct, rather than just moving the field. This is to aid in predictability, so that removing the move doesn't make the closure become unsafe: ```rust -print(&packed.x); -move_value(packed.x); +#[repr(packed)] +struct Packed { x: String } + +# fn use_ref(_: &T) {} +# fn move_value(_: T) {} + +fn main() { + let packed = Packed { x: String::new() }; + + let c = || { + use_ref(&packed.x); + move_value(packed.x); + }; + + c(); +} +``` + + +```ignore +Closure mode = ref +C = { + (ref mut, packed) +} +C' = C ``` +### Optimization-Edge-Case +```edition2021 +struct Int(i32); +struct B<'a>(&'a i32); -```rust -struct Point { x: i32, y: i32 } -fn f(p: &Point) -> impl Fn() { - let c = move || { - let x = p.x; - }; - - // x.x -> ByValue - // after rules x -> ByValue +struct MyStruct<'a> { + a: &'static Int, + b: B<'a>, +} +fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { + let c = || drop(&m.a.0); c -} +} -struct Point { x: i32, y: i32 } -fn g(p: &mut Point) -> impl Fn() { - let c = move || { - let x = p.x; // ought to: (ref, (*p).x) - }; - - move || { - p.y += 1; - } - - - // x.x -> ByValue - +``` + + +```ignore +Closure mode = ref +C = { + (ref mut, *m.a) +} +C' = C ``` # Edition 2018 and before From 7944512783684dc48cff00d7b2a8d898f67c4705 Mon Sep 17 00:00:00 2001 From: Roxane Date: Tue, 20 Jul 2021 14:41:15 -0400 Subject: [PATCH 28/32] fix stray merge conflict --- src/types/closure.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/types/closure.md b/src/types/closure.md index 91007ec6fd..f8accef837 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -57,11 +57,10 @@ f(Closure{ left_top: rect.left_top, right_bottom_x: rect.left_top.x }); The compiler prefers to capture a value by immutable borrow, followed by unique immutable borrow (see below), by mutable borrow, and finally -by move. It will pick the first choice of these that allows the closure to -compile. The choice is made only with regards to the contents of the closure -expression; the compiler does not take into account surrounding code, such as -the lifetimes of involved variables or fields. ->>>>>>> 881f305... Update closure types documentation so it includes information about RFC2229 +by move. It will pick the first choice of these that is compatible with how the +captured value is used inside the closure body. The compiler does not take +surrounding code into account, such as the lifetimes of involved variables or fields, or +of the closure itself. ## Capture Precision From 55c0829f2cb1d70b272562c239a4be5c85629bf4 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Mon, 2 Aug 2021 03:11:26 -0400 Subject: [PATCH 29/32] Minor edits from Feedback --- src/types/closure.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/types/closure.md b/src/types/closure.md index f8accef837..4407567359 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -317,7 +317,7 @@ If a closure captures a field of a composite types such as structs, tuples, and * Input: * Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed * Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max) - * For a `Place` that is used in two different acess modes within the same closure, the mode reported from closure analysis is the maximum access mode. + * For a `Place` that is used in two different access modes within the same closure, the mode reported from closure analysis is the maximum access mode. * Note: `ByValue` use of a `Copy` type is seen as a `ref` access mode. * Closure mode is `ref` or `move` * Output: @@ -383,6 +383,8 @@ If a closure captures a field of a composite types such as structs, tuples, and ### box-mut +This test shows how a `move` closure can sometimes capture values by mutable reference, if they are reached via a `&mut` reference. + ```rust struct Foo { x: i32 } @@ -405,10 +407,10 @@ fn box_mut() { ```ignore Closure mode = move -C = { +C_in = { (ref mut, (*(*bx)).x) } -C' = C +C_out = C_in ``` Output is the same: `C' = C` @@ -439,13 +441,16 @@ fn main() { ```ignore Closure mode = ref -C = { +C_in = { (ref mut, packed) } -C' = C +C_out = C_in ``` ### Optimization-Edge-Case + +This test shows an interesting edge case. Normally, when we see a borrow of something behind a shared reference (`&T`), we truncate to capture the entire reference, because that is more efficient (and we can always use that reference to reach all the data it refers to). However, in the case where we are dereferencing two shared references, we have to be sure to preserve the full path, since otherwise the resulting closure could have a shorter lifetime than is necessary. + ```edition2021 struct Int(i32); struct B<'a>(&'a i32); @@ -465,10 +470,10 @@ fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { ```ignore Closure mode = ref -C = { +C_in = { (ref mut, *m.a) } -C' = C +C_out = C_in ``` # Edition 2018 and before From fe03349f87ec84916f3ff1cfe813ea0fd40e6700 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Mon, 2 Aug 2021 05:12:31 -0400 Subject: [PATCH 30/32] Add details on truncation and ref-uniq --- src/types/closure.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/types/closure.md b/src/types/closure.md index 4407567359..c37f18f5eb 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -337,12 +337,12 @@ If a closure captures a field of a composite types such as structs, tuples, and * Helper functions: * `unsafe_check(Place, Mode) -> (Place, Mode)` * "Ensure unsafe accesses occur within the closure" - * If Place contains a deref of a raw pointer: - * Let Place1 = Place truncated just before the deref - * Return (Place1, Mode) - * If Mode is `ref *` and the place contains a field of a packed struct: - * Let Place1 = Place truncated just before the field - * Return (Place1, Mode) + * If Place contains a deref (at index `i`) of a raw pointer: + * Let `(Place1, Mode1) = truncate_place(Place, Mode, i)` + * Return (Place1, Mode1) + * If Mode is `ref *` and the place contains a field of a packed struct at index `i`: + * Let `(Place1, Mode1) = truncate_place(Place, Mode, i)` + * Return (Place1, Mode1) * Else * Return (Place, Mode) * `move_xform(Place, Mode) -> (Place, Mode)` (For move closures) @@ -353,16 +353,16 @@ If a closure captures a field of a composite types such as structs, tuples, and * Return (Place, Mode) * Else if Mode is `ref *` and the place contains a deref of an `&`: * Return (Place, Mode) - * Else if place contains a deref: - * Let Place1 = Place truncated just before the deref + * Else if place contains a deref at index `i`: + * Let `(Place1, _) = truncate_place(Place, Mode, i)` * Return (Place1, ByValue) * Else: * Return (Place, ByValue) * `ref_xform(Place, Mode) -> (Place, Mode)` (for ref closures) * "If taking ownership of data, only move data from enclosing stack frame." * Generate C' by mapping each (Mode, Place) in C - * If Mode is ByValue and place contains a deref: - * Let Place1 = Place truncated just before the deref + * If Mode is ByValue and place contains a deref at index `i`: + * Let `(Place1, _) = truncate_place(Place, Mode, i)` * Return (Place1, ByValue) * Else: * Return (Place, Mode) @@ -378,6 +378,13 @@ If a closure captures a field of a composite types such as structs, tuples, and and `Place.type_before_projection(l) = ty::Ref(.., Mutability::Not)` * Let Place1 = (Base, Projections[0..=l]) * Return (Place1, Ref) + * `truncate_place(Place, Mode, len) -> (Place, Mode)` + * "Truncate the place to length `len`, i.e. upto but not including index `len`" + * "If during truncation we drop Deref of a `&mut` and the place was being used by `ref mut`, the access to the truncated place must be unique" + * Let (Proj_before, Proj_after) = Place.split_before(len) + * If Mode == `ref mut` and there exists `Deref` in `Proj_after` at index `i` such that `Place.type_before_projection(len + i)` is `&mut T` + * Return (Place(Proj_before, ..InputPlace), `ref-uniq`) + * Else Return (Place(Proj_before, ..InputPlace), Mode) ## Key examples From 702a71f187d74defc6a134cb8e48680d33bfb399 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Wed, 22 Sep 2021 00:54:10 -0700 Subject: [PATCH 31/32] Update closure algorithm per #88467 #88477 --- src/types/closure.md | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/types/closure.md b/src/types/closure.md index c37f18f5eb..ed5538cc53 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -106,14 +106,14 @@ let c = || match x { ### Capturing references in move contexts -Moving fields out of references is not allowed. As a result, in the case of move closures, when values accessed through a shared references are moved into the closure body, the compiler, instead of moving the values out of the reference, would reborrow the data. +Moving fields out of references is not allowed. As a result, in the case of move closures, when values accessed through a shared references are moved into the closure body, the compiler will truncate right before a dereference. ```rust struct T(String, String); let mut t = T(String::from("foo"), String::from("bar")); let t = &mut t; -let c = move || t.0.truncate(0); // closure captures (&mut t.0) +let c = move || t.0.truncate(0); // closure captures `t` ``` ### Raw pointer dereference @@ -325,10 +325,11 @@ If a closure captures a field of a composite types such as structs, tuples, and * Cleanup and truncation * Generate C' by mapping each (Mode, Place) in C: * `(Place1, Mode1) = ref_opt(unsafe_check(Place, Mode))` - * if this is a ref closure: - * Add `ref_xform(Place1, Mode1)` to C' + * `(Place2, Mode2)` = if this is a ref closure: + * `ref_xform(Place1, Mode1)` * else: - * Add `move_xform(Place1, Mode1)` to C' + * `move_xform(Place1, Mode1)` + * Add `(Place3, Mode3) = truncate_move_through_drop(Place2, Mode2)` to C'. * Minimization * Until no rules apply: * For each two places (P1, M1), (P2, M2) where P1 is a prefix of P2: @@ -346,18 +347,12 @@ If a closure captures a field of a composite types such as structs, tuples, and * Else * Return (Place, Mode) * `move_xform(Place, Mode) -> (Place, Mode)` (For move closures) - * "Take ownership if data being accessed is owned by the variable used to access it (or if closure attempts to move data that it doesn't own)." - * "When taking ownership, only capture data found on the stack." - * "Otherwise, reborrow the reference." - * If Mode is `ref mut` and the place contains a deref of an `&mut`: - * Return (Place, Mode) - * Else if Mode is `ref *` and the place contains a deref of an `&`: - * Return (Place, Mode) - * Else if place contains a deref at index `i`: + * If place contains a deref at index `i`: * Let `(Place1, _) = truncate_place(Place, Mode, i)` * Return (Place1, ByValue) * Else: * Return (Place, ByValue) + * Note that initially we had considered an approach where "Take ownership if data being accessed is owned by the variable used to access it (or if closure attempts to move data that it doesn't own). That is when taking ownership only capture data that is found on the stack otherwise reborrow the reference.". This cause a bug around lifetimes, check [rust-lang/rust#88431](https://github.com/rust-lang/rust/issues/88431). * `ref_xform(Place, Mode) -> (Place, Mode)` (for ref closures) * "If taking ownership of data, only move data from enclosing stack frame." * Generate C' by mapping each (Mode, Place) in C @@ -378,6 +373,16 @@ If a closure captures a field of a composite types such as structs, tuples, and and `Place.type_before_projection(l) = ty::Ref(.., Mutability::Not)` * Let Place1 = (Base, Projections[0..=l]) * Return (Place1, Ref) + * `truncate_move_through_drop(Place1, Mode1) -> (Place, Mode)` + * Rust doesn't permit moving out of a type that implements drop + * In the case where we do a disjoint capture in a move closure, we might end up trying to move out of drop type + * We truncate move of not-Copy types + * If Mode1 != ByBalue + * return (Place1, Mode1) + * If there exists `i` such that `Place1.before_projection(i): Drop` and `Place1.ty()` doesn't impl `Copy` + * then return `truncate_place(Place1, Mode1, i)` + * Else return (Place1, Mode1) + * Check [rust-lang/rust#88476](https://github.com/rust-lang/rust/issues/88476) for examples. * `truncate_place(Place, Mode, len) -> (Place, Mode)` * "Truncate the place to length `len`, i.e. upto but not including index `len`" * "If during truncation we drop Deref of a `&mut` and the place was being used by `ref mut`, the access to the truncated place must be unique" @@ -397,11 +402,11 @@ struct Foo { x: i32 } fn box_mut() { let mut s = Foo { x: 0 } ; - + let px = &mut s; let bx = Box::new(px); - - + + let c = move || bx.x += 10; // Mutable reference to this place: // (*(*bx)).x From 44f6e1e20f40dddf1b5e9aa002d5cd6e7a629663 Mon Sep 17 00:00:00 2001 From: Aman Arora Date: Fri, 19 Nov 2021 14:06:04 -0800 Subject: [PATCH 32/32] Update src/types/closure.md Co-authored-by: Josh Triplett --- src/types/closure.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/closure.md b/src/types/closure.md index ed5538cc53..ecdb337196 100644 --- a/src/types/closure.md +++ b/src/types/closure.md @@ -7,7 +7,7 @@ closure: ```rust #[derive(Debug)] -struct Point { x:i32, y:i32 } +struct Point { x: i32, y: i32 } struct Rectangle { left_top: Point, right_bottom: Point } fn f String> (g: F) {