Skip to content

Commit 4984678

Browse files
authored
Merge pull request #1981 from dianne/extending-match
specify lifetime extension of `match` arms and `if` consequent/`else` block tail expressions
2 parents 0ec5eba + 1071e07 commit 4984678

File tree

2 files changed

+84
-17
lines changed

2 files changed

+84
-17
lines changed

docs/authoring.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ See <https://rust-lang.github.io/mdBook/format/theme/syntax-highlighting.html#su
4747

4848
Rust examples are tested via rustdoc, and should include the appropriate annotations:
4949

50-
* `edition2015` or `edition2018` --- If it is edition-specific (see `book.toml` for the default).
50+
* `edition2015`, `edition2018`, etc. --- If it is edition-specific (see `book.toml` for the default).
5151
* `no_run` --- The example should compile successfully, but should not be executed.
5252
* `should_panic` --- The example should compile and run, but produce a panic.
5353
* `compile_fail` --- The example is expected to fail to compile.
5454
* `ignore` --- The example shouldn't be built or tested. This should be avoided if possible. Usually this is only necessary when the testing framework does not support it (such as external crates or modules, or a proc-macro), or it contains pseudo-code which is not valid Rust. An HTML comment such as `<!-- ignore: requires extern crate -->` should be placed before the example to explain why it is ignored.
5555
* `Exxxx` --- If the example is expected to fail to compile with a specific error code, include that code so that rustdoc will check that the expected code is used.
5656

57+
When demonstrating success cases, many such cases may be included in a single code block. For failure cases, however, each example must appear in a separate code block so that the tests can ensure that each case indeed fails and fails with the appropriate error code or codes.
58+
5759
See the [rustdoc documentation] for more detail.
5860

5961
[rustdoc documentation]: https://doc.rust-lang.org/rustdoc/documentation-tests.html

src/destructors.md

Lines changed: 81 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -434,48 +434,111 @@ expression which is one of the following:
434434
expression], [braced struct][struct expression], or [tuple][tuple expression]
435435
expression.
436436
* The arguments to an extending [tuple struct] or [tuple variant] constructor expression.
437-
* The final expression of any extending [block expression].
437+
* The final expression of an extending [block expression] except for an [async block expression].
438+
* The final expression of an extending [`if`] expression's consequent, `else if`, or `else` block.
439+
* An arm expression of an extending [`match`] expression.
438440

439441
So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some(&mut 3)`
440442
are all extending expressions. The borrows in `&0 + &1` and `f(&mut 0)` are not.
441443

442444
The operand of any extending borrow expression has its temporary scope
443445
extended.
444446

447+
> [!NOTE]
448+
> `rustc` does not treat [array repeat operands] of extending [array] expressions as extending expressions. Whether it should is an open question.
449+
>
450+
> For details, see [Rust issue #146092](https://github.com/rust-lang/rust/issues/146092).
451+
445452
#### Examples
446453

447454
Here are some examples where expressions have extended temporary scopes:
448455

449-
```rust
450-
# fn temp() {}
451-
// The temporary that stores the result of `temp()` lives in the same scope
452-
// as x in these cases.
453-
let x = &temp();
456+
```rust,edition2024
457+
# use core::sync::atomic::{AtomicU64, Ordering::Relaxed};
458+
# static X: AtomicU64 = AtomicU64::new(0);
459+
# struct S;
460+
# impl Drop for S { fn drop(&mut self) { X.fetch_add(1, Relaxed); } }
461+
# const fn temp() -> S { S }
462+
let x = &temp(); // Operand of borrow.
463+
# x;
464+
let x = &raw const *&temp(); // Operand of raw borrow.
465+
# assert_eq!(X.load(Relaxed), 0);
466+
let x = &temp() as &dyn Send; // Operand of cast.
467+
# x;
468+
let x = (&*&temp(),); // Operand of tuple constructor.
469+
# x;
470+
let x = { [Some(&temp())] }; // Final expr of block.
471+
# x;
472+
let x = const { &temp() }; // Final expr of `const` block.
454473
# x;
455-
let x = &temp() as &dyn Send;
474+
let x = unsafe { &temp() }; // Final expr of `unsafe` block.
456475
# x;
457-
let x = (&*&temp(),);
476+
let x = if true { &temp() } else { &temp() };
477+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
478+
// Final exprs of `if`/`else` blocks.
458479
# x;
459-
let x = { [Some(&temp()) ] };
480+
let x = match () { _ => &temp() }; // `match` arm expression.
460481
# x;
461-
let ref x = temp();
482+
let ref x = temp(); // Initializer expression.
462483
# x;
463-
let ref x = *&temp();
484+
let ref x = *&temp(); // Initializer expression.
464485
# x;
486+
//
487+
// All of the temporaries above are still live here.
488+
# assert_eq!(X.load(Relaxed), 0);
465489
```
466490

467491
Here are some examples where expressions don't have extended temporary scopes:
468492

469-
```rust,compile_fail
493+
```rust,compile_fail,E0716
494+
# fn temp() {}
495+
// Arguments to function calls are not extending expressions. The
496+
// temporary is dropped at the semicolon.
497+
let x = core::convert::identity(&temp()); // ERROR
498+
# x;
499+
```
500+
501+
```rust,compile_fail,E0716
470502
# fn temp() {}
471503
# trait Use { fn use_temp(&self) -> &Self { self } }
472504
# impl Use for () {}
473-
// The temporary that stores the result of `temp()` only lives until the
474-
// end of the let statement in these cases.
505+
// Receivers of method calls are not extending expressions.
506+
let x = (&temp()).use_temp(); // ERROR
507+
# x;
508+
```
475509

476-
let x = std::convert::identity(&temp()); // ERROR
510+
```rust,compile_fail,E0716
511+
# fn temp() {}
512+
// Scrutinees of match expressions are not extending expressions.
513+
let x = match &temp() { x => x }; // ERROR
477514
# x;
478-
let x = (&temp()).use_temp(); // ERROR
515+
```
516+
517+
```rust,compile_fail,E0515
518+
# fn temp() {}
519+
// Final expressions of `async` blocks are not extending expressions.
520+
let x = async { &temp() }; // ERROR
521+
# x;
522+
```
523+
524+
```rust,compile_fail,E0515
525+
# fn temp() {}
526+
// Final expressions of closures are not extending expressions.
527+
let x = || &temp(); // ERROR
528+
# x;
529+
```
530+
531+
```rust,compile_fail,E0716
532+
# fn temp() {}
533+
// Operands of loop breaks are not extending expressions.
534+
let x = loop { break &temp() }; // ERROR
535+
# x;
536+
```
537+
538+
```rust,compile_fail,E0716
539+
# fn temp() {}
540+
// Operands of breaks to labels are not extending expressions.
541+
let x = 'a: { break 'a &temp() }; // ERROR
479542
# x;
480543
```
481544

@@ -536,6 +599,8 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi
536599
[tuple variant]: type.enum.declaration
537600

538601
[array expression]: expressions/array-expr.md#array-expressions
602+
[array repeat operands]: expr.array.repeat-operand
603+
[async block expression]: expr.block.async
539604
[block expression]: expressions/block-expr.md
540605
[borrow expression]: expressions/operator-expr.md#borrow-operators
541606
[cast expression]: expressions/operator-expr.md#type-cast-expressions

0 commit comments

Comments
 (0)