Skip to content

Commit 3cbc57d

Browse files
committed
s/datums/data/
1 parent 7df9596 commit 3cbc57d

File tree

1 file changed

+15
-15
lines changed

1 file changed

+15
-15
lines changed

posts/2024-09-05-impl-trait-capture-rules.md

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ The use of `-> impl Iterator` in return position here means that the function re
3535

3636
Although callers don't know the exact type, they do need to know that it will continue to borrow the `data` argument so that they can ensure that the `data` reference remains valid while iteration occurs. Further, callers must be able to figure this out based solely on the type signature, without looking at the function body.
3737

38-
Rust's current rules are that a return-position `impl Trait` value can only use a reference if the lifetime of that reference appears in the `impl Trait` itself. In this example, `impl Iterator<Item = ProcessedDatum>` does not reference any lifetimes, and therefore capturing `datums` is illegal. You can see this for yourself [on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2448fc4ec9e763c538aaba897433f9b5).
38+
Rust's current rules are that a return-position `impl Trait` value can only use a reference if the lifetime of that reference appears in the `impl Trait` itself. In this example, `impl Iterator<Item = ProcessedDatum>` does not reference any lifetimes, and therefore capturing `data` is illegal. You can see this for yourself [on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2448fc4ec9e763c538aaba897433f9b5).
3939

4040
The error message ("hidden type captures lifetime") you get in this scenario is not the most intuitive, but it does come with a useful suggestion for how to fix it:
4141

@@ -53,21 +53,21 @@ Following a slightly more explicit version of this advice, the function signatur
5353

5454

5555
```rust
56-
fn process_datums<'d>(
57-
datums: &'d [Datum]
56+
fn process_data<'d>(
57+
data: &'d [Datum]
5858
) -> impl Iterator<Item = ProcessedDatum> + 'd {
59-
datums
59+
data
6060
.iter()
6161
.map(|datum| datum.process())
6262
}
6363
```
6464

65-
In this version, the lifetime `'d` of the datums is explicitly referenced in the `impl Trait` type, and so it is allowed to be used. This is also a signal to the caller that the borrow for `datums` must last as long as the iterator is in use, which means that it (correctly) flags an error in an example like this ([try it on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=afd9278ac887c0b2fc08bc868200808f)):
65+
In this version, the lifetime `'d` of the data is explicitly referenced in the `impl Trait` type, and so it is allowed to be used. This is also a signal to the caller that the borrow for `data` must last as long as the iterator is in use, which means that it (correctly) flags an error in an example like this ([try it on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=afd9278ac887c0b2fc08bc868200808f)):
6666

6767
```rust
68-
let mut datums: Vec<Datum> = vec![Datum::default()];
69-
let iter = process_datums(&datums);
70-
datums.push(Datum::default()); // <-- Error!
68+
let mut data: Vec<Datum> = vec![Datum::default()];
69+
let iter = process_data(&data);
70+
data.push(Datum::default()); // <-- Error!
7171
iter.next();
7272
```
7373

@@ -94,17 +94,17 @@ Adding a `+ '_` argument to `impl Trait` may be confusing, but it's not terribly
9494
```rust
9595
fn process<'c, T> {
9696
context: &'c Context,
97-
datums: Vec<T>,
97+
data: Vec<T>,
9898
) -> impl Iterator<Item = ()> + 'c {
99-
datums
99+
data
100100
.into_iter()
101101
.map(|datum| context.process(datum))
102102
}
103103
```
104104

105-
Here the `process` function applies `context.process` to each of the elements in `datums` (of type `T`). Because the return value uses `context`, it is declared as `+ 'c`. Our real goal here is to allow the return type to use `'c`; writing `+ 'c` achieves that goal because `'c` not appears in the bound listing. However, while writing `+ 'c` is a convenient way to make `'c` appear in the bounds, also means that the hidden type must outlive `'c`. This requirement is not needed and will in fact lead to a compilation error in this example ([try it on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b742fbf9b083a6e837db0b170489f34a)).
105+
Here the `process` function applies `context.process` to each of the elements in `data` (of type `T`). Because the return value uses `context`, it is declared as `+ 'c`. Our real goal here is to allow the return type to use `'c`; writing `+ 'c` achieves that goal because `'c` not appears in the bound listing. However, while writing `+ 'c` is a convenient way to make `'c` appear in the bounds, also means that the hidden type must outlive `'c`. This requirement is not needed and will in fact lead to a compilation error in this example ([try it on the playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b742fbf9b083a6e837db0b170489f34a)).
106106

107-
The reason that this error occurs is a bit subtle. The hidden type is an iterator type based on the result of `datums.into_iter()`, which will include the type `T`. Because of the `+ 'c` bound, the hidden type must outlive `'c`, which in turn means that `T` must outlive `'c`. But `T` is a generic parameter, so the compiler requires a where-clause like `where T: 'c`. This where-clause means "it is safe to create a reference with lifetime `'c` to the type `T`". But in fact we don't create any such reference, so the where-clause should not be needed. It is only needed because used the convenient-but-sometimes-incorrect workaround of adding `+ 'c` to the bounds of our `impl Trait`.
107+
The reason that this error occurs is a bit subtle. The hidden type is an iterator type based on the result of `data.into_iter()`, which will include the type `T`. Because of the `+ 'c` bound, the hidden type must outlive `'c`, which in turn means that `T` must outlive `'c`. But `T` is a generic parameter, so the compiler requires a where-clause like `where T: 'c`. This where-clause means "it is safe to create a reference with lifetime `'c` to the type `T`". But in fact we don't create any such reference, so the where-clause should not be needed. It is only needed because used the convenient-but-sometimes-incorrect workaround of adding `+ 'c` to the bounds of our `impl Trait`.
108108

109109
Just as before, this error is obscure, touching on the more complex aspects of Rust's type system. Unlike before, there is no easy fix! This problem in fact occurred frequently in the compiler, leading to an [obscure workaround called the `Captures` trait](https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999). Gross!
110110

@@ -154,10 +154,10 @@ The new explicit syntax is called a "use bound": `impl Trait + use<'x, T>`, for
154154
In Rust 2024, the default is that the hidden type for a return-position `impl Trait` values use **any** generic parameter that is in scope, whether it is a type or a lifetime. This means that the initial example of this blog post will compile just fine in Rust 2024 ([try it yourself by setting the Edition in the Playground to 2024](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=d366396da2fbd5334b7560c3dfb3290b)):
155155

156156
```rust
157-
fn process_datums(
158-
datums: &[Datum]
157+
fn process_data(
158+
data: &[Datum]
159159
) -> impl Iterator<Item = ProcessedDatum> {
160-
datums
160+
data
161161
.iter()
162162
.map(|datum| datum.process())
163163
}

0 commit comments

Comments
 (0)