Skip to content

Commit 8970b99

Browse files
committed
Add section on precise capturing use<..> syntax
Thanks to CE for details on the compiler's use-case that informs an example here.
1 parent 307525a commit 8970b99

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

posts/2024-10-17-Rust-1.82.0.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,83 @@ The Rust target `aarch64-apple-darwin` for macOS on 64-bit ARM (M1-family or lat
5757

5858
[The targets](https://doc.rust-lang.org/nightly/rustc/platform-support/apple-ios-macabi.html) are now tier 2, and can be downloaded with `rustup target add aarch64-apple-ios-macabi x86_64-apple-ios-macabi`, so now is an excellent time to update your CI pipeline to test that your code also runs in iOS-like environments.
5959

60+
### Precise capturing `use<..>` syntax
61+
62+
Rust now supports `use<..>` syntax within certain impl Trait bounds to control which generic lifetime parameters are captured.
63+
64+
Return-position impl Trait (RPIT) types in Rust *capture* certain generic parameters. Capturing a generic parameter allows that parameter to be used in the hidden type. That in turn affects borrow checking.
65+
66+
In Rust 2021 and earlier editions, lifetime parameters are not captured in opaque types on bare functions and on functions and methods of inherent impls unless those lifetime parameters are mentioned syntactically in the opaque type. E.g., this is an error:
67+
68+
```rust
69+
//@ edition: 2021
70+
fn f(x: &()) -> impl Sized { x }
71+
```
72+
73+
```
74+
error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
75+
--> src/main.rs:1:30
76+
|
77+
1 | fn f(x: &()) -> impl Sized { x }
78+
| --- ---------- ^
79+
| | |
80+
| | opaque type defined here
81+
| hidden type `&()` captures the anonymous lifetime defined here
82+
|
83+
help: add a `use<...>` bound to explicitly capture `'_`
84+
|
85+
1 | fn f(x: &()) -> impl Sized + use<'_> { x }
86+
| +++++++++
87+
```
88+
89+
With the new `use<..>` syntax, we can fix this, as suggested in the error, by writing:
90+
91+
```rust
92+
fn f(x: &()) -> impl Sized + use<'_> { x }
93+
```
94+
95+
Previously, correctly fixing this class of error required defining a dummy trait, conventionally called `Captures`, and using it as follows:
96+
97+
```rust
98+
trait Captures<T: ?Sized> {}
99+
impl<T: ?Sized, U: ?Sized> Captures<T> for U {}
100+
101+
fn f(x: &()) -> impl Sized + Captures<&'_ ()> { x }
102+
```
103+
104+
That was called ["the `Captures` trick"](https://github.com/rust-lang/rfcs/blob/master/text/3498-lifetime-capture-rules-2024.md#the-captures-trick), and it was a bit baroque and subtle. It's no longer needed.
105+
106+
There was a less correct way but more convenient way to fix this that was often used called ["the outlives trick"](https://github.com/rust-lang/rfcs/blob/master/text/3498-lifetime-capture-rules-2024.md#the-outlives-trick). The compiler even previously suggested doing this. That trick looked like this:
107+
108+
```rust
109+
fn f(x: &()) -> impl Sized + '_ { x }
110+
```
111+
112+
In this simple case, the trick is exactly equivalent to `+ use<'_>` for subtle reasons explained in [RFC 3498](https://github.com/rust-lang/rfcs/blob/master/text/3498-lifetime-capture-rules-2024.md). However, in real life cases, this overconstrains the bounds on the returned opaque type, leading to problems. For example, consider this code, which is inspired by a real case in the Rust compiler:
113+
114+
```rust
115+
struct Ctx<'cx>(&'cx u8);
116+
117+
fn f<'cx, 'a>(
118+
cx: Ctx<'cx>,
119+
x: &'a u8,
120+
) -> impl Iterator<Item = &'a u8> + 'cx {
121+
core::iter::once_with(move || {
122+
eprintln!("LOG: {}", cx.0);
123+
x
124+
})
125+
//~^ ERROR lifetime may not live long enough
126+
}
127+
```
128+
129+
We can't remove the `+ 'cx`, since the lifetime is used in the hidden type and so must be captured. Neither can we add a bound of `'a: 'cx`, since these lifetimes are not actually related and it won't in general be true that `'a` outlives `'cx`. If we write `+ use<'cx, 'a>` instead, however, this will work and have the correct bounds.
130+
131+
There are some limitations to what we're stabilizing today. The `use<..>` syntax cannot currently appear within traits or within trait impls (but note that there, in-scope lifetime parameters are already captured by default), and it must list all in-scope generic type and const parameters. We hope to lift these restrictions over time.
132+
133+
Note that in Rust 2024, the examples above will "just work" without needing `use<..>` syntax (or any tricks). This is because in the new edition, opaque types will automatically capture all lifetime parameters in scope. This is a better default, and we've seen a lot of evidence about how this cleans up code. In Rust 2024, `use<..>` syntax will serve as an important way of opting-out of that default.
134+
135+
For more details about `use<..>` syntax, capturing, and how this applies to Rust 2024, see the ["RPIT lifetime capture rules"](https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rpit-lifetime-capture.html) chapter of the edition guide. For details about the overall direction, see our recent blog post, ["Changes to `impl Trait` in Rust 2024"](https://blog.rust-lang.org/2024/09/05/impl-trait-capture-rules.html).
136+
60137
### Native syntax for creating a raw pointer
61138

62139
Unsafe code sometimes has to deal with pointers that may dangle, may be misaligned, or may not point to valid data. A common case where this comes up are `repr(packed)` structs. In such a case, it is important to avoid creating a reference, as that would cause undefined behavior. This means the usual `&` and `&mut` operators cannot be used, as those create a reference -- even if the reference is immediately cast to a raw pointer, it's too late to avoid the undefined behavior.

0 commit comments

Comments
 (0)