From cbc1941768e37e290757ed87ca3379277ecef4b0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 28 Jul 2024 16:41:29 -0700 Subject: [PATCH 01/36] Simplify lightweight clones, including closures and async blocks --- text/0000-use.md | 364 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 text/0000-use.md diff --git a/text/0000-use.md b/text/0000-use.md new file mode 100644 index 00000000000..0233bfe6189 --- /dev/null +++ b/text/0000-use.md @@ -0,0 +1,364 @@ +- Feature Name: `use` +- Start Date: 2024-07-20 +- 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 + +Provide a feature to simplify performing lightweight clones (such as of +`Arc`/`Rc`), particularly cloning them into closures or async blocks, while +still keeping such cloning visible and explicit. + +# Motivation +[motivation]: #motivation + +A very common source of friction in asynchronous or multithreaded Rust +programming is having to clone various `Arc` reference-counted objects into +an async block or task. This is particularly common when spawning a closure as +a thread, or spawning an async block as a task. Common patterns for doing so +include: + +```rust +// Use new names throughout the block +let new_x = x.clone(); +let new_y = y.clone(); +spawn(async move { + func1(new_x).await; + func2(new_y).await; +}); + +// Introduce a scope to perform the clones in +{ + let x = x.clone(); + let y = y.clone(); + spawn(async move { + func1(x).await; + func2(y).await; + }); +} + +// Introduce a scope to perform the clones in, inside the call +spawn({ + let x = x.clone(); + let y = y.clone(); + async move { + func1(x).await; + func2(y).await; + } +}); +``` + +All of these patterns introduce noise every time the program wants to spawn a +thread or task, or otherwise clone an object into a closure or async block. +Feedback on Rust regularly brings up this friction, seeking a simpler solution. + +In addition, Rust developers trying to avoid heavyweight clones will sometimes +suggest eschewing invocations of `obj.clone()` in favor of writing +`Arc::clone(&obj)` explicitly, to mark the call explicitly as a lightweight +clone, at the cost of syntactic salt. This RFC proposes a syntax that can +*only* make a lightweight clone, while still using a simple postfix syntax. + +In some cases, people ask for fully *automatic* cloning, requiring no visible +indication at the point of the clone. However, Rust has long attempted to keep +user-provided code visible, such as by not providing copy constructors. Rust +users regularly provide feedback on this point as well, asking for clones to +not become implicit, or otherwise confirming that they appreciate the absence +of copy constructors. + +This RFC proposes solutions to *minimize* the syntactic weight of +lightweight-cloning objects, particularly cloning objects into a closure or +async block, while still keeping an indication of this operation. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +When working with objects that support lightweight cloning, such as `Rc` or +`Arc`, you can get an additional clone (a new "use") of the object by invoking +`.use`: + +```rust +let obj: Arc = new_large_complex_object(); +some_function(obj.use); // Pass a separate use of the object to `some_function` +obj.method(); // The object is still owned afterwards +``` + +If you want to create a closure or async block that captures new uses of such +objects, you can put the `use` keyword on the closure or async block, similar +to the `move` keyword. + +```rust +let obj: Arc = new_large_complex_object(); +let map: Arc = new_mapping(); +std::thread::spawn(use || map.insert(42, func(obj))); +task::spawn(async use { op(map, obj).await }); +another_func(obj, map); +``` + +(Note that `use` and `move` are mutually exclusive.) + +`.use` supports chaining, so in particular it works when calling a method that +would otherwise consume `self`: + +```rust +obj.use.consume(); +obj.method(); +``` + +Calling `x.use` requires that the type of `x` implement the `Use` trait. This +trait identifies types whose clone implementation is lightweight, such as +reference-counted types. + +Various types in the standard library implement `Use`. You can implement this +trait for your own types, if they meet the requirements for being lightweight +to clone. (See the [reference-level explanation][reference-level-explanation] +for the requirements.) + +```rust +impl Use for MyType {} +``` + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +## The `Use` trait + +```rust +/// Trait for objects whose clone impl is lightweight (e.g. reference-counted) +/// +/// Cloning an object implementing this trait should in general: +/// - be O(1) (constant) time regardless of the amount of data managed by the object, +/// - not require a memory allocation, +/// - not require copying more than roughly 64 bytes (a typical cache line size), +/// - not block, +/// - not have any semantic side effects (e.g. allocating a file descriptor), and +/// - not have overhead larger than a couple of atomic operations. +trait Use: Clone { + /// Add a use of the object, and return the new object + /// + /// Note that implementers of the trait cannot override this method; it + /// will always call `Clone::clone`. + pub impl(crate) do_use(&self) -> Self { + Clone::clone(self) + } +} +``` + +Note that while this uses the `impl(crate)` syntax proposed in RFC FIXME, this +is not a dependency for stabilization; we can use any mechanism that prevents +implementation of `Use::do_use()` in impl blocks outside the standard library. + +This trait should be implemented for anything in the standard library that +meets these criteria. Some notable types in the standard library that implement +`Use`: + +- `std::sync::Arc` +- `std::sync::Weak` +- `std::rc::Rc` +- `std::rc::Weak` +- `std::sync::mpsc::Sender` and `std::sync::mpsc::SyncSender` +- Tuples of types whose components all implement `Use` +- `Option` where `T: Use` +- `Result` where `T: Use` and `E: Use` + +Some notable types that implement `Clone` but *don't* implement `Use`: arrays, +`String`, `Box`, `Vec`, `HashMap`, and `BTreeMap`. + +## The implementation and optimization of `.use` + +An expression `x.use`, where `x` has type `T`, requires that `T: Use`. However, +`x.use` does not always invoke `Use::do_use(x)`; in some cases the compiler can +optimize away a use. + +If `x` is statically known to be dead, the compiler will move `x` rather than +using it. This allows functions to write `x.use` without concern for whether +it's the last usage of `x` (e.g. when passing `x.use` to a series of +functions). Much like a trailing comma, this allows every usage to be +symmetric, making it easy to compare uses or add more uses afterwards. (This +optimization also means we should generally not lint on a final use of `x.use`, +such as we currently do with the clippy lint `redundant_clone`.) + +If `x` is not statically known to be dead, but *all* of the following +conditions are met, the compiler *may* elide an `x.use` and use `&x` instead: +- The compiler can statically see that `x` outlives the result of `x.use`, +- The compiler can statically see that the result of `x.use` is only accessed + via shared reference (including methods with `&self`, dereferences via + `Deref`, other invocations of `.use`, `use ||` closures, or `async use` + blocks). Effectively, these conditions mean that the user could theoretically + have refactored the code to use `&x` rather than `x.use`. + +An example of these elisions: + +```rust +fn f(obj: Arc) { + g(obj.use); // `g` takes `Arc` + h(use || obj.method()); // `Object::method` here takes `&self` +} + +fn main() { + let obj = Arc::new(Object::new()); + f(obj.use); + f(obj.use); + f(obj.use); + g(obj.use); +} +``` + +If every invocation of `.use` or `use ||` here resulted in a call to +`.do_use()`, this program would call `.do_use()` 10 times (and have 11 `Arc`s +to drop); however, the compiler can elide all the uses in `main` and have `f` +work with the original Arc, resulting in only 6 calls to `.do_use()` (and only +7 `Arc`s to drop). When an object is used repeatedly, such as in a loop, this +can result in many elisions over the course of the program, and lower +contention for atomics. + +If a user has an unusual `Use` type for which they wish to avoid these +potential elisions, they can call `.do_use()` directly. (In a future edition of +Rust, `Use` will be in the prelude, allowing calls to `.do_use()` without +importing anything.) + +At any time, we could potentially instrument the compiler to detect the number +of elided calls to `.do_use()` in a crater run, to demonstrate the value of +this optimization. + +## `use ||` closures and `async use` blocks + +A closure can be written as `use |args| ...`, and an async block can be written +as `async use { ... }`, analogous to the use of `move`. (Note that `use` and +`move` are mutually exclusive.) + +For any object referenced within the closure or block that a `move` +closure/block would move, `use` will `.use` that object and have the closure +own the new use of the object. If any object referenced within the closure or +block does not implement `Use` (including generic types whose bounds do not +require `Use`), the closure or block will attempt to borrow that object instead +(as it would do without `move`/`use`). If that borrow results in a +borrow-checker error, the compiler will report the error stating that *either* +the object must implement `Use` or it must outlive the closure/block. + +Note in particular that this allows the same closure to `use` an `Arc` and +borrow a large array. Without the fallback to attempting a borrow, it would not +be possible to borrow a large array. + +# Drawbacks +[drawbacks]: #drawbacks + +This adds language surface area. + +While this still makes lightweight clones visible, it makes them *less* +visible. (See "Rationale and alternatives".) + +Users may misuse this by implementing `Use` for a type that doesn't meet the +requirements. Not all of the requirements can be checked by the compiler. While +this is still *safe*, it may result in types that violate user's expectations. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +We could do nothing, and require people to continue calling `.clone()` and +introducing new bindings for closures and async blocks. + +There are myriad names we could use for the trait, method, invocation syntax, +and closure / async block syntax. An ideal name for this needs to convey the +semantic of taking an additional reference to an object, without copying or +duplicating the object. Other names proposed for this mechanism include +`Claim`. + +We could use a name that directly references referencing counting; for +instance, `AddRef`/`add_ref`. However, that would be confusing for applications +using this with objects managed by something other than reference counting, +such as RCU or hazard pointers, or with (small) objects being copied. + +Rather than using the special syntax `.use`, we could use an ordinary trait +method and invoke that method directly (e.g. `.claim()`). The syntax provided +for closures and async blocks could likewise always invoke that method. This +would not be compatible with adding smarter semantics such as eliding uses, +however. In addition, `.use` can be used in all editions of Rust (because `use` +is a reserved keyword), while a new trait with a method like `.claim()` would +require an import in existing editions and could only become part of the +prelude in a future edition. + +Rather than having a single keyword in `use ||` or `async use`, we could +require naming every individual object being used (e.g. `use(x, y) ||`). +There's precedent for this kind of explicit capture syntax in other languages +(and having it as an available *option* is in the +[future possibilities][future-possibilities] section). However, requiring a +list of every object used adds overhead to closures and async blocks throughout +a program. This RFC proposes that the single keyword `use` suffices to indicate +that lightweight cloning will take place. + +Rather than having `Use` act as `Clone` and go from `&self` to `Self`, we could +translate through a trait like `ToOwned`. This would allow using `Use` when +owned values have a different type, such as `&MyType -> SmartPtr`. +However, this would also add complexity to the common case. + +We could omit the elision optimizations, and have `.use` *always* call +`.do_use()` unconditionally. This would be slightly simpler, but would add +unnecessary overhead, and would encourage users to micro-optimize their code by +arranging to omit calls to `.use`. The elision optimizations encourage users to +always call `.use`. + +In the elision optimizations, we could potentially allow the last usage of the +result of `x.use` to take ownership of it, and the compiler could insert a +`.use` at that point. However, at that point the elision would not have +resulted in any fewer calls to `.use`, so this does not seem worth the extra +complexity. + +# Prior art +[prior-art]: #prior-art + +Many languages have built-in reference counting, and automatically manage +reference counts when copying or passing around objects. `.use` provides a +simple and efficient way for Rust to manage reference counts. + +Some languages have copy constructors, allowing arbitrary code to run when +copying an object. Such languages can manage reference-counted smart pointers +implicitly. + +Rust already has other uses of postfix syntax, notably `.await` and `?`. In +particular, `.await` provides precedent for the use of `.keyword`. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +- What parts of the design do you expect to resolve through the RFC process before this gets merged? +- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? +- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? + +# Future possibilities +[future-possibilities]: #future-possibilities + +We could allow `.use` in struct construction without duplicating the name of a +field. For instance: + +```rust +let s = SomeStruct { field, field2.use, field3.use }; +/// This expands to: +let s = SomeStruct { + field: field, + field2: field2.use, + field3: field3.use, +}; +``` + +We could extend the `use` syntax on closures and async blocks to support +naming specific objects to use: + +```rust +use(x) || { ... } + +async use(x) { ... } +``` + +We could further extend the `use` syntax to support an explicit capture list of +named expressions: + +```rust +use(x = x.method(), y) || { ... } + +// `..` additionally captures everything that a plain `use` would +async use(x = &obj, move y, ..) +``` + +We could consider providing a syntax to make invocations like `func(a.use, +b.use, c.use)` less verbose. From 396cc149f36bfdb3b8811e6feca43981944e38bb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 13:00:43 -0700 Subject: [PATCH 02/36] Add RFC number for cross-reference to RFC 3678 --- text/0000-use.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-use.md b/text/0000-use.md index 0233bfe6189..a641c250750 100644 --- a/text/0000-use.md +++ b/text/0000-use.md @@ -144,7 +144,7 @@ trait Use: Clone { } ``` -Note that while this uses the `impl(crate)` syntax proposed in RFC FIXME, this +Note that while this uses the `impl(crate)` syntax proposed in RFC 3678, this is not a dependency for stabilization; we can use any mechanism that prevents implementation of `Use::do_use()` in impl blocks outside the standard library. From 07679f11a0a977d306077a6451d8a9307e3151b0 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 13:01:03 -0700 Subject: [PATCH 03/36] Clarify wording --- text/0000-use.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-use.md b/text/0000-use.md index a641c250750..e9597c76778 100644 --- a/text/0000-use.md +++ b/text/0000-use.md @@ -161,8 +161,8 @@ meets these criteria. Some notable types in the standard library that implement - `Option` where `T: Use` - `Result` where `T: Use` and `E: Use` -Some notable types that implement `Clone` but *don't* implement `Use`: arrays, -`String`, `Box`, `Vec`, `HashMap`, and `BTreeMap`. +Some notable types that implement `Clone` but should *not* implement `Use`: +arrays, `String`, `Box`, `Vec`, `HashMap`, and `BTreeMap`. ## The implementation and optimization of `.use` From f3b96684c6dfc2902d709b1cc215787323cd8509 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 13:01:09 -0700 Subject: [PATCH 04/36] Add future possibility of `Use` on small arrays --- text/0000-use.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-use.md b/text/0000-use.md index e9597c76778..8ef08e9efbe 100644 --- a/text/0000-use.md +++ b/text/0000-use.md @@ -328,6 +328,8 @@ particular, `.await` provides precedent for the use of `.keyword`. # Future possibilities [future-possibilities]: #future-possibilities +We could implement `Use` for small arrays (e.g. arrays smaller than 64 bytes). + We could allow `.use` in struct construction without duplicating the name of a field. For instance: From 8ad586210fd1212c243ea65aeb42a32ad3fc6bae Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 13:07:50 -0700 Subject: [PATCH 05/36] Expand on evaluation of use cases in future work --- text/0000-use.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/0000-use.md b/text/0000-use.md index 8ef08e9efbe..91caabbdf96 100644 --- a/text/0000-use.md +++ b/text/0000-use.md @@ -363,4 +363,6 @@ async use(x = &obj, move y, ..) ``` We could consider providing a syntax to make invocations like `func(a.use, -b.use, c.use)` less verbose. +b.use, c.use)` less verbose. In many cases, such a function could accept a +reference and call `.use` itself if needed, but we should evaluate whether +there are use cases not covered by that. From 6fa409af22a48922e14188aad8587364a0029123 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 13:17:38 -0700 Subject: [PATCH 06/36] Record `ToOwned` possibility as an unresolved question --- text/0000-use.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/text/0000-use.md b/text/0000-use.md index 91caabbdf96..6a0844dcbf4 100644 --- a/text/0000-use.md +++ b/text/0000-use.md @@ -321,9 +321,8 @@ particular, `.await` provides precedent for the use of `.keyword`. # Unresolved questions [unresolved-questions]: #unresolved-questions -- What parts of the design do you expect to resolve through the RFC process before this gets merged? -- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? -- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? +Are there use cases for using something like `ToOwned` rather than always going +from `&self` to `Self`? Would any smart pointers need that? # Future possibilities [future-possibilities]: #future-possibilities From fd42e86ca5abba675e72f3797735c3401bd2da39 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 13:22:05 -0700 Subject: [PATCH 07/36] Suggest a lint for expensive `Use` implementations --- text/0000-use.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text/0000-use.md b/text/0000-use.md index 6a0844dcbf4..d998bf69d38 100644 --- a/text/0000-use.md +++ b/text/0000-use.md @@ -164,6 +164,18 @@ meets these criteria. Some notable types in the standard library that implement Some notable types that implement `Clone` but should *not* implement `Use`: arrays, `String`, `Box`, `Vec`, `HashMap`, and `BTreeMap`. +We may want to add a clippy or rustc lint (e.g. `expensive_use`) for +implementations of `Use` on an excessively large type, or a type whose `clone` +implementation seems to be obviously breaking the intended constraints on the +`Use` trait. Such a lint would be best-effort only, and could always be marked +as `allow` by a crate, but could help to discourage such implementations. + +We may want to add a clippy or rustc lint for calls to `.clone()` that could +use `.use` instead. This would help the remaining calls to `.clone()` stand out +as "expensive" clones. (Such a lint would need to take MSRV into account before +making such a suggestion; clippy already has such a mechanism and rustc may +gain one in the future.) + ## The implementation and optimization of `.use` An expression `x.use`, where `x` has type `T`, requires that `T: Use`. However, From 64ed357d6496dafea4e9a2d2c75737f3484ab995 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 14:31:32 -0700 Subject: [PATCH 08/36] RFC 3680 --- text/{0000-use.md => 3680-use.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-use.md => 3680-use.md} (100%) diff --git a/text/0000-use.md b/text/3680-use.md similarity index 100% rename from text/0000-use.md rename to text/3680-use.md From 71461317f2170c99c9005647c830edef4c685c2d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 14:36:19 -0700 Subject: [PATCH 09/36] Add RFC number to RFC PR link --- text/3680-use.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3680-use.md b/text/3680-use.md index d998bf69d38..31584d63610 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -1,6 +1,6 @@ - Feature Name: `use` - Start Date: 2024-07-20 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#3680](https://github.com/rust-lang/rfcs/pull/3680) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary From 5b8539037ffd8aec57bc252671a706983e15efbf Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 14:36:41 -0700 Subject: [PATCH 10/36] Add alternative for calling `Clone::clone` directly --- text/3680-use.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 31584d63610..90a3ead4b64 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -281,6 +281,13 @@ instance, `AddRef`/`add_ref`. However, that would be confusing for applications using this with objects managed by something other than reference counting, such as RCU or hazard pointers, or with (small) objects being copied. +Rather than having the method `Use::do_use`, we could have the compiler +directly call `Clone::clone` on a `Use` type. Since `Use` has `Clone` as a +supertrait, any type implementing `Use` will implement `Clone`. This would +avoid needing to have an un-overridable method in the `Use` trait. Users who +want to avoid the elision behavior of `.use` could then call `.clone()` rather +than `.do_use()`. + Rather than using the special syntax `.use`, we could use an ordinary trait method and invoke that method directly (e.g. `.claim()`). The syntax provided for closures and async blocks could likewise always invoke that method. This From 5bae2bfec93a075f031fef788e3c46814018cf52 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 15:00:16 -0700 Subject: [PATCH 11/36] Use `Clone::clone` directly; eliminate `Use::do_use` --- text/3680-use.md | 66 ++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 38 deletions(-) diff --git a/text/3680-use.md b/text/3680-use.md index 90a3ead4b64..7d2bd61a3e8 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -133,21 +133,12 @@ impl Use for MyType {} /// - not block, /// - not have any semantic side effects (e.g. allocating a file descriptor), and /// - not have overhead larger than a couple of atomic operations. -trait Use: Clone { - /// Add a use of the object, and return the new object - /// - /// Note that implementers of the trait cannot override this method; it - /// will always call `Clone::clone`. - pub impl(crate) do_use(&self) -> Self { - Clone::clone(self) - } -} +/// +/// The `Use` trait does not provide a method; instead, it indicates that +/// `Clone::clone` is lightweight, and allows the use of the `.use` syntax. +trait Use: Clone {} ``` -Note that while this uses the `impl(crate)` syntax proposed in RFC 3678, this -is not a dependency for stabilization; we can use any mechanism that prevents -implementation of `Use::do_use()` in impl blocks outside the standard library. - This trait should be implemented for anything in the standard library that meets these criteria. Some notable types in the standard library that implement `Use`: @@ -179,8 +170,8 @@ gain one in the future.) ## The implementation and optimization of `.use` An expression `x.use`, where `x` has type `T`, requires that `T: Use`. However, -`x.use` does not always invoke `Use::do_use(x)`; in some cases the compiler can -optimize away a use. +`x.use` does not always invoke `Clone::clone(x)`; in some cases the compiler +can optimize away a use. If `x` is statically known to be dead, the compiler will move `x` rather than using it. This allows functions to write `x.use` without concern for whether @@ -217,20 +208,18 @@ fn main() { ``` If every invocation of `.use` or `use ||` here resulted in a call to -`.do_use()`, this program would call `.do_use()` 10 times (and have 11 `Arc`s -to drop); however, the compiler can elide all the uses in `main` and have `f` -work with the original Arc, resulting in only 6 calls to `.do_use()` (and only -7 `Arc`s to drop). When an object is used repeatedly, such as in a loop, this -can result in many elisions over the course of the program, and lower -contention for atomics. +`Clone::clone`, this program would call `Clone::clone` 10 times (and have 11 +`Arc`s to drop); however, the compiler can elide all the uses in `main` and +have `f` work with the original Arc, resulting in only 6 calls to +`Clone::clone` (and only 7 `Arc`s to drop). When an object is used repeatedly, +such as in a loop, this can result in many elisions over the course of the +program, and lower contention for atomics. If a user has an unusual `Use` type for which they wish to avoid these -potential elisions, they can call `.do_use()` directly. (In a future edition of -Rust, `Use` will be in the prelude, allowing calls to `.do_use()` without -importing anything.) +potential elisions, they can call `.clone()` directly. At any time, we could potentially instrument the compiler to detect the number -of elided calls to `.do_use()` in a crater run, to demonstrate the value of +of elided calls to `Clone::clone` in a crater run, to demonstrate the value of this optimization. ## `use ||` closures and `async use` blocks @@ -270,23 +259,24 @@ this is still *safe*, it may result in types that violate user's expectations. We could do nothing, and require people to continue calling `.clone()` and introducing new bindings for closures and async blocks. -There are myriad names we could use for the trait, method, invocation syntax, -and closure / async block syntax. An ideal name for this needs to convey the +There are myriad names we could use for the trait, invocation syntax, and +closure / async block syntax. An ideal name for this needs to convey the semantic of taking an additional reference to an object, without copying or duplicating the object. Other names proposed for this mechanism include `Claim`. We could use a name that directly references referencing counting; for -instance, `AddRef`/`add_ref`. However, that would be confusing for applications -using this with objects managed by something other than reference counting, -such as RCU or hazard pointers, or with (small) objects being copied. - -Rather than having the method `Use::do_use`, we could have the compiler -directly call `Clone::clone` on a `Use` type. Since `Use` has `Clone` as a -supertrait, any type implementing `Use` will implement `Clone`. This would -avoid needing to have an un-overridable method in the `Use` trait. Users who -want to avoid the elision behavior of `.use` could then call `.clone()` rather -than `.do_use()`. +instance, `AddRef`. However, that would be confusing for applications using +this with objects managed by something other than reference counting, such as +RCU or hazard pointers, or with (small) objects being copied. + +Rather than having the method `Clone::clone`, we could have a method +`Use::do_use` and have the compiler call that. We could prevent users from +overriding that method implementation, so that it always calls `Clone::clone`; +however, having a separate `do_use` method would allow users to call +`.do_use()` (when wanting to avoid the elision of `.use`) while knowing they +can't invoke an expensive clone operation. This does not seem worth the +additional complexity, since most users should invoke `.use` directly. Rather than using the special syntax `.use`, we could use an ordinary trait method and invoke that method directly (e.g. `.claim()`). The syntax provided @@ -312,7 +302,7 @@ owned values have a different type, such as `&MyType -> SmartPtr`. However, this would also add complexity to the common case. We could omit the elision optimizations, and have `.use` *always* call -`.do_use()` unconditionally. This would be slightly simpler, but would add +`Clone::clone` unconditionally. This would be slightly simpler, but would add unnecessary overhead, and would encourage users to micro-optimize their code by arranging to omit calls to `.use`. The elision optimizations encourage users to always call `.use`. From 969836b529925e877a745eb5d84ef5d9e733a3e2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 15:43:07 -0700 Subject: [PATCH 12/36] Add an alternative for general `Clone::clone` elision --- text/3680-use.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 7d2bd61a3e8..c7036020396 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -313,6 +313,16 @@ result of `x.use` to take ownership of it, and the compiler could insert a resulted in any fewer calls to `.use`, so this does not seem worth the extra complexity. +Rather than adding elision behavior for `.use`, we could add elision behavior +for specifically designated `Clone` implementations (e.g. with an attribute). +However, this would leave users unable to make an *un*-elided call to `clone`. +And if we added a mechanism to bypass this elision, the result would likely +have comparable complexity to the addition of a separate trait. Nonetheless, +there's potential for possible optimization here, and that optimization might +benefit expensive clones as well (e.g. eliding the clone of a `String` if the +`String` being cloned is statically known to be dead). We should explore +potential optimizations here. + # Prior art [prior-art]: #prior-art From 823d892a7c64e02221f2e918b2785fce09db82bb Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 16:06:11 -0700 Subject: [PATCH 13/36] Add alternative/rationale explaining why automatic clone doesn't suffice --- text/3680-use.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index c7036020396..97e39b24f48 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -259,6 +259,18 @@ this is still *safe*, it may result in types that violate user's expectations. We could do nothing, and require people to continue calling `.clone()` and introducing new bindings for closures and async blocks. +Rather than specifically supporting *lightweight* clones, we could add a syntax +for closures and async blocks to perform *any* clones (e.g. `async clone` / +`clone ||`). This would additionally allow expensive clones (such as +`String`/Vec`). However, we've had many requests to distinguish between +expensive and lightweight clones, as well as ecosystem conventions attempting +to make such distinctions (e.g. past guidance to write `Arc::clone`/`Rc::clone` +explicitly). Having a syntax that only permits lightweight clones would allow +users to confidently use that syntax without worrying about an unexpectedly +expensive operation. We can then provide ways to perform the expensive clones +explicitly, such as the `use(x = x.clone())` syntax suggested in +[future possibilities][future-possibilities]. + There are myriad names we could use for the trait, invocation syntax, and closure / async block syntax. An ideal name for this needs to convey the semantic of taking an additional reference to an object, without copying or From 7d41d87e3d1ec99acb2c5e7da5d6def59aaaa955 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 16:13:33 -0700 Subject: [PATCH 14/36] Add the possibility of using a method while still having elision --- text/3680-use.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 97e39b24f48..1a004b352c3 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -299,6 +299,12 @@ is a reserved keyword), while a new trait with a method like `.claim()` would require an import in existing editions and could only become part of the prelude in a future edition. +We could use an ordinary trait method *and* add a new set of semantics attached +to that trait method, such that the compiler is allowed to elide calls to the +trait method. This would avoid the introduction of new language syntax for +`.use`, at the cost of making something that looks like an ordinary method call +have semantics beyond those normally associated with a method call. + Rather than having a single keyword in `use ||` or `async use`, we could require naming every individual object being used (e.g. `use(x, y) ||`). There's precedent for this kind of explicit capture syntax in other languages From de930d89c49853849407f0b097b6c93d19fcb6db Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 16:26:57 -0700 Subject: [PATCH 15/36] Alternative: constrain the elision rules and disallow future optimization --- text/3680-use.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 1a004b352c3..c0327ef5ea9 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -341,6 +341,14 @@ benefit expensive clones as well (e.g. eliding the clone of a `String` if the `String` being cloned is statically known to be dead). We should explore potential optimizations here. +Rather than making the elision optimizations optional and allowing for future +improvements to them, we could mandate those optimizations and specify the +exact elision rules. This would, however, constrain our ability to improve +elision in the future, as well as requiring full implementation of those +elision rules before shipping the feature. This would not be the first Rust +mechanism whose implementation is subject to change, and leaving it flexible +allows the Rust compiler to improve in the future. + # Prior art [prior-art]: #prior-art From da366a03fd1c77b5edb9f18b577ebc399f6509a5 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 16:28:00 -0700 Subject: [PATCH 16/36] Fix typo (missing backquote) --- text/3680-use.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3680-use.md b/text/3680-use.md index c0327ef5ea9..ec86273fe31 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -262,7 +262,7 @@ introducing new bindings for closures and async blocks. Rather than specifically supporting *lightweight* clones, we could add a syntax for closures and async blocks to perform *any* clones (e.g. `async clone` / `clone ||`). This would additionally allow expensive clones (such as -`String`/Vec`). However, we've had many requests to distinguish between +`String`/`Vec`). However, we've had many requests to distinguish between expensive and lightweight clones, as well as ecosystem conventions attempting to make such distinctions (e.g. past guidance to write `Arc::clone`/`Rc::clone` explicitly). Having a syntax that only permits lightweight clones would allow From 7f2da747784bba5bfa4c98d2eadf4b5d3a032596 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 16:43:16 -0700 Subject: [PATCH 17/36] Remove fallback to borrowing, and add an unresolved question --- text/3680-use.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/text/3680-use.md b/text/3680-use.md index ec86273fe31..cffdc934daa 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -230,16 +230,7 @@ as `async use { ... }`, analogous to the use of `move`. (Note that `use` and For any object referenced within the closure or block that a `move` closure/block would move, `use` will `.use` that object and have the closure -own the new use of the object. If any object referenced within the closure or -block does not implement `Use` (including generic types whose bounds do not -require `Use`), the closure or block will attempt to borrow that object instead -(as it would do without `move`/`use`). If that borrow results in a -borrow-checker error, the compiler will report the error stating that *either* -the object must implement `Use` or it must outlive the closure/block. - -Note in particular that this allows the same closure to `use` an `Arc` and -borrow a large array. Without the fallback to attempting a borrow, it would not -be possible to borrow a large array. +own the new use of the object. # Drawbacks [drawbacks]: #drawbacks @@ -366,6 +357,17 @@ particular, `.await` provides precedent for the use of `.keyword`. # Unresolved questions [unresolved-questions]: #unresolved-questions +How should we handle borrows within `use` closures and `async use` blocks? The +goal would be to borrow thing for which a borrow suffices, and `.use` things +that need to be `use`d; the primary benefit of this would be the ability to use +a `use` closure or `async use` block with, for instance, a large array, if the +large array only needs to be borrowed. On the other hand, if it's *possible* to +borrow the large array, it may well be possible to borrow other objects as +well, in which case that closure/block may not need `use`; thus, we may not +need a solution for this use case, or at least we may not need a solution +urgently and we can wait for the future possibility of `use(...)` syntax to +handle the array by name. + Are there use cases for using something like `ToOwned` rather than always going from `&self` to `Self`? Would any smart pointers need that? From 4cc8920d4ff8bd0106df1a8b0ed95f503dbbd0a2 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Aug 2024 16:50:59 -0700 Subject: [PATCH 18/36] Add alternative for adding this to bindings rather than types --- text/3680-use.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index cffdc934daa..b57547df4ec 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -290,6 +290,14 @@ is a reserved keyword), while a new trait with a method like `.claim()` would require an import in existing editions and could only become part of the prelude in a future edition. +Rather than attaching this behavior to types (e.g. `Arc` and `Rc`), we could +attach this behavior to *bindings*. For instance, we could have `let +somekeyword x = Arc::new(...);` and make it easy to clone such bindings into +closures and async blocks. However, this would add additional noise to every +such binding, when users are likely to want this behavior for *every* binding +of a given type (e.g. every `Arc`). In addition, this would require adding such +an annotation to struct fields and similar. + We could use an ordinary trait method *and* add a new set of semantics attached to that trait method, such that the compiler is allowed to elide calls to the trait method. This would avoid the introduction of new language syntax for From dc8745bbc416b497985c2bd689bcf6bf7d206b01 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 09:36:04 -0700 Subject: [PATCH 19/36] Add possibility of direct `Copy` (per nikomatsakis) --- text/3680-use.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index b57547df4ec..221388b4af1 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -348,6 +348,10 @@ elision rules before shipping the feature. This would not be the first Rust mechanism whose implementation is subject to change, and leaving it flexible allows the Rust compiler to improve in the future. +We could specify that for `Copy` types, `use` always copies directly, ignoring +any `Clone` implementation. Would this have an advantage over calling `Clone` +and relying on that generally turning into a copy for `Copy` types? + # Prior art [prior-art]: #prior-art From 122cf55e821ab1171a5a2eab2c245164199346df Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 09:39:33 -0700 Subject: [PATCH 20/36] Add possibility of closures/blocks moving things if dead --- text/3680-use.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 221388b4af1..e9502e2cb55 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -352,6 +352,12 @@ We could specify that for `Copy` types, `use` always copies directly, ignoring any `Clone` implementation. Would this have an advantage over calling `Clone` and relying on that generally turning into a copy for `Copy` types? +We could specify that, for a `use` closure or `async use` block, if a capture +is statically dead after the closure/block, it gets moved rather than being +`use`d, and thus doesn't require `Use`. This would make, for instance, arrays +or other values requiring a move work automatically (which has advantages and +disadvantages). + # Prior art [prior-art]: #prior-art From ab000b9c16c62f5f512fbf443885258489b09b20 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 09:52:38 -0700 Subject: [PATCH 21/36] Rework motivation to make the RFC more neutral towards future RFCs --- text/3680-use.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/text/3680-use.md b/text/3680-use.md index e9502e2cb55..2dc8fa7377e 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -59,12 +59,13 @@ suggest eschewing invocations of `obj.clone()` in favor of writing clone, at the cost of syntactic salt. This RFC proposes a syntax that can *only* make a lightweight clone, while still using a simple postfix syntax. -In some cases, people ask for fully *automatic* cloning, requiring no visible -indication at the point of the clone. However, Rust has long attempted to keep -user-provided code visible, such as by not providing copy constructors. Rust -users regularly provide feedback on this point as well, asking for clones to -not become implicit, or otherwise confirming that they appreciate the absence -of copy constructors. +This RFC does *not* provide fully automatic/invisible cloning. Some users have +asked for that, wanting no visible indication at the point of the clone. +However, other users have asked that this *not* happen, and appreciate that +Rust does not have implicit clones or copy/move constructors. This RFC does not +change this, and does not provide any precedent or motivation for changing +this; any proposal to change this would need its own additional motivation and +detailed exploration of tradeoffs. This RFC proposes solutions to *minimize* the syntactic weight of lightweight-cloning objects, particularly cloning objects into a closure or From 7825d315e74d5339b39fe826f9375515761bc723 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 10:13:50 -0700 Subject: [PATCH 22/36] Future possibility: lints to help catch large array copies --- text/3680-use.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 2dc8fa7377e..d1b30cf1cec 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -395,6 +395,10 @@ from `&self` to `Self`? Would any smart pointers need that? We could implement `Use` for small arrays (e.g. arrays smaller than 64 bytes). +We could consider using lints or similar to help avoid unexpected expensive +copies of *large* arrays or other large `Copy` types, potentially using `Use` +as a marker for what is not expensive. + We could allow `.use` in struct construction without duplicating the name of a field. For instance: From 3ef0e567083115deebfcdd3ee91b36f520bd8bf1 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 10:43:21 -0700 Subject: [PATCH 23/36] Add more general explorations of Copy/Use --- text/3680-use.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/text/3680-use.md b/text/3680-use.md index d1b30cf1cec..7c2af085cd3 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -397,7 +397,9 @@ We could implement `Use` for small arrays (e.g. arrays smaller than 64 bytes). We could consider using lints or similar to help avoid unexpected expensive copies of *large* arrays or other large `Copy` types, potentially using `Use` -as a marker for what is not expensive. +as a marker for what is not expensive. More generally, we could explore the +space of types with various combinations of `Use` and `Copy`, and what we want +`Copy + Use` vs `Copy + !Use` to indicate. We could allow `.use` in struct construction without duplicating the name of a field. For instance: From ca8240839be1cc3d6075762d8f79b978a5e2b70d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 10:44:16 -0700 Subject: [PATCH 24/36] Aligns with other proposals for precise capture syntax --- text/3680-use.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 7c2af085cd3..1e2cc78cca0 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -433,6 +433,9 @@ use(x = x.method(), y) || { ... } async use(x = &obj, move y, ..) ``` +This could also align with other proposals for precise capture syntax (e.g. +`use(ref x)`). + We could consider providing a syntax to make invocations like `func(a.use, b.use, c.use)` less verbose. In many cases, such a function could accept a reference and call `.use` itself if needed, but we should evaluate whether From 10671f4f531ecbca0a8a7c10ad51c33de984fe8a Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 10:47:06 -0700 Subject: [PATCH 25/36] Closures/blocks move if statically dead --- text/3680-use.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/text/3680-use.md b/text/3680-use.md index 1e2cc78cca0..054273d9cb0 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -230,8 +230,12 @@ as `async use { ... }`, analogous to the use of `move`. (Note that `use` and `move` are mutually exclusive.) For any object referenced within the closure or block that a `move` -closure/block would move, `use` will `.use` that object and have the closure -own the new use of the object. +closure/block would move: +- If the object is statically dead, ownership gets moved into the + closure/block. +- Otherwise, it gets `.use`ed, and the closure owns the new use of the object. +- If it isn't statically dead and doesn't implement `Use`, this produces an + error. # Drawbacks [drawbacks]: #drawbacks @@ -353,12 +357,6 @@ We could specify that for `Copy` types, `use` always copies directly, ignoring any `Clone` implementation. Would this have an advantage over calling `Clone` and relying on that generally turning into a copy for `Copy` types? -We could specify that, for a `use` closure or `async use` block, if a capture -is statically dead after the closure/block, it gets moved rather than being -`use`d, and thus doesn't require `Use`. This would make, for instance, arrays -or other values requiring a move work automatically (which has advantages and -disadvantages). - # Prior art [prior-art]: #prior-art From 80745d6e16976cb5a0cdf6292343938f16b680d8 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 10:48:22 -0700 Subject: [PATCH 26/36] Clarify "statically dead" to "statically dead after the closure/block" --- text/3680-use.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3680-use.md b/text/3680-use.md index 054273d9cb0..e83f7430f8f 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -231,8 +231,8 @@ as `async use { ... }`, analogous to the use of `move`. (Note that `use` and For any object referenced within the closure or block that a `move` closure/block would move: -- If the object is statically dead, ownership gets moved into the - closure/block. +- If the object is statically dead after the closure/block, ownership gets + moved into the closure/block. - Otherwise, it gets `.use`ed, and the closure owns the new use of the object. - If it isn't statically dead and doesn't implement `Use`, this produces an error. From de7d33b8f8e61d380c553f7c0620bd6d97136a9c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 10:55:23 -0700 Subject: [PATCH 27/36] Superseding `move` closures? --- text/3680-use.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index e83f7430f8f..f9dc4b07367 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -434,6 +434,9 @@ async use(x = &obj, move y, ..) This could also align with other proposals for precise capture syntax (e.g. `use(ref x)`). +This potentially makes `use` closures/blocks almost completely supersede `move` +closures; we could consider in the future whether we want to deprecate them. + We could consider providing a syntax to make invocations like `func(a.use, b.use, c.use)` less verbose. In many cases, such a function could accept a reference and call `.use` itself if needed, but we should evaluate whether From a130f07c2619de97e62404457737bb1d9574a2ec Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 10:57:05 -0700 Subject: [PATCH 28/36] Unresolved question: `#[marker]` trait? --- text/3680-use.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index f9dc4b07367..136ec05ecb0 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -388,6 +388,8 @@ handle the array by name. Are there use cases for using something like `ToOwned` rather than always going from `&self` to `Self`? Would any smart pointers need that? +Should `Use` be a `#[marker]` trait? + # Future possibilities [future-possibilities]: #future-possibilities From 1fc1d6afd9bfd18a732bc7ccc4c006105d66d5fa Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 11:41:53 -0700 Subject: [PATCH 29/36] Add alternative of floating `x.use` out of closures --- text/3680-use.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 136ec05ecb0..463c985debb 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -357,6 +357,11 @@ We could specify that for `Copy` types, `use` always copies directly, ignoring any `Clone` implementation. Would this have an advantage over calling `Clone` and relying on that generally turning into a copy for `Copy` types? +We could make `x.use` inside of a closure cause the closure to get a new use of +`x` even if it otherwise could borrow, effectively "floating" the `use` outside +of the closure. This would have different semantics, such as if the closure +mutated the object before the `use`, so deciding this is a one-way door. + # Prior art [prior-art]: #prior-art From b7b847431d89d72c9a8cbfe63046a2367647974e Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 11:42:36 -0700 Subject: [PATCH 30/36] Unresolved questions: remove a resolved question --- text/3680-use.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/text/3680-use.md b/text/3680-use.md index 463c985debb..2a64fb1b820 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -379,17 +379,6 @@ particular, `.await` provides precedent for the use of `.keyword`. # Unresolved questions [unresolved-questions]: #unresolved-questions -How should we handle borrows within `use` closures and `async use` blocks? The -goal would be to borrow thing for which a borrow suffices, and `.use` things -that need to be `use`d; the primary benefit of this would be the ability to use -a `use` closure or `async use` block with, for instance, a large array, if the -large array only needs to be borrowed. On the other hand, if it's *possible* to -borrow the large array, it may well be possible to borrow other objects as -well, in which case that closure/block may not need `use`; thus, we may not -need a solution for this use case, or at least we may not need a solution -urgently and we can wait for the future possibility of `use(...)` syntax to -handle the array by name. - Are there use cases for using something like `ToOwned` rather than always going from `&self` to `Self`? Would any smart pointers need that? From 84a5614d994845eb9876d130278ceadc92f7956d Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 11:49:18 -0700 Subject: [PATCH 31/36] Add unresolved question for clone elision --- text/3680-use.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 2a64fb1b820..fc330b3c36a 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -379,6 +379,14 @@ particular, `.await` provides precedent for the use of `.keyword`. # Unresolved questions [unresolved-questions]: #unresolved-questions +Should we allow *any* `x.clone()` call to be elided if a type is `Use` (or `Use ++ Copy`)? If we did this, that would not give us a way to explicitly avoid +elision (unless we added a separate mechanism for that), but it would also mean +that we could avoid tracking whether a clone call came from `use` or not when +applying the elision rules, which might make the implementation simpler. (This +RFC proposes not allowing explicit `x.clone()` calls to be elided, so that +users can make explicit `x.clone()` calls to avoid elision if desired.) + Are there use cases for using something like `ToOwned` rather than always going from `&self` to `Self`? Would any smart pointers need that? From 16d8f54fe5cf5148a10c5df666fe911450fb67bd Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 11:52:34 -0700 Subject: [PATCH 32/36] Expand mention of `Copy` handling in alternatives --- text/3680-use.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index fc330b3c36a..e46a73e0a88 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -356,6 +356,8 @@ allows the Rust compiler to improve in the future. We could specify that for `Copy` types, `use` always copies directly, ignoring any `Clone` implementation. Would this have an advantage over calling `Clone` and relying on that generally turning into a copy for `Copy` types? +(Interacting with that: in a future edition, would we want to make `.clone()` +of a `Copy` type ignore the `Clone` implementation and just copy?) We could make `x.use` inside of a closure cause the closure to get a new use of `x` even if it otherwise could borrow, effectively "floating" the `use` outside From d9faf4c16d699c66809c06b232cbb9fa58ef2623 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 11:55:35 -0700 Subject: [PATCH 33/36] Clarify an elision case --- text/3680-use.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/text/3680-use.md b/text/3680-use.md index e46a73e0a88..a06e5d58005 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -174,10 +174,10 @@ An expression `x.use`, where `x` has type `T`, requires that `T: Use`. However, `x.use` does not always invoke `Clone::clone(x)`; in some cases the compiler can optimize away a use. -If `x` is statically known to be dead, the compiler will move `x` rather than -using it. This allows functions to write `x.use` without concern for whether -it's the last usage of `x` (e.g. when passing `x.use` to a series of -functions). Much like a trailing comma, this allows every usage to be +If `x` is owned and statically known to be dead, the compiler will move `x` +rather than using it. This allows functions to write `x.use` without concern +for whether it's the last usage of `x` (e.g. when passing `x.use` to a series +of functions). Much like a trailing comma, this allows every usage to be symmetric, making it easy to compare uses or add more uses afterwards. (This optimization also means we should generally not lint on a final use of `x.use`, such as we currently do with the clippy lint `redundant_clone`.) From 856093b5e379cf32d1f2f807b874297142ae75ae Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 13:06:22 -0700 Subject: [PATCH 34/36] We may want a derive(Use) --- text/3680-use.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index a06e5d58005..4c2e85bb7ad 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -168,6 +168,9 @@ as "expensive" clones. (Such a lint would need to take MSRV into account before making such a suggestion; clippy already has such a mechanism and rustc may gain one in the future.) +We may want to add a `derive(Use)`, which automatically adds `Use` bounds on +fields just as `derive(Clone)` adds `Clone` bounds on fields. + ## The implementation and optimization of `.use` An expression `x.use`, where `x` has type `T`, requires that `T: Use`. However, From 9d5166dd28b49b08cfbf3b05de2ec5bd6dd45a09 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 13:06:35 -0700 Subject: [PATCH 35/36] Future work: Smarter suggestions for missing bounds --- text/3680-use.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 4c2e85bb7ad..7ab08545179 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -450,3 +450,8 @@ We could consider providing a syntax to make invocations like `func(a.use, b.use, c.use)` less verbose. In many cases, such a function could accept a reference and call `.use` itself if needed, but we should evaluate whether there are use cases not covered by that. + +When adding an `impl Use for Type`, if the compiler would otherwise error due +to a missing `Clone` bound, we may want the compiler to suggest a `Use` bound +instead. (The same problem applies to other traits; for instance, an `impl Eq +for Type` may want to suggest an `Eq` bound rather than a `PartialEq` bound.) From 041a8406171c97baa54a522ca10b4cbc5de58055 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Wed, 21 Aug 2024 13:56:51 -0700 Subject: [PATCH 36/36] Mention alternate keywords --- text/3680-use.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/text/3680-use.md b/text/3680-use.md index 7ab08545179..a32f24abff4 100644 --- a/text/3680-use.md +++ b/text/3680-use.md @@ -276,6 +276,12 @@ semantic of taking an additional reference to an object, without copying or duplicating the object. Other names proposed for this mechanism include `Claim`. +We could use a different keyword, if one fits. For instance, we could use `ref` +rather than `use`. However, this 1) might over-fit to the concept of +reference-counting specifically, rather than lightweight clones, 2) might +confuse users who expect it to produce a `&` reference, and 3) for +closures/blocks, would not generalize to explicit capture lists. + We could use a name that directly references referencing counting; for instance, `AddRef`. However, that would be confusing for applications using this with objects managed by something other than reference counting, such as