Skip to content

Commit 76cb0ae

Browse files
committed
Discuss the use<..> impl Trait alternative
In the T-lang design meeting on 2024-04-24, a new syntax option was raised: `use<..> impl Trait`. While some people liked this, others did not, and no clear consensus formed to change the main proposal in this RFC. Nevertheless, let's discuss this as an alternative.
1 parent dc8f93d commit 76cb0ae

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

text/3617-precise-capturing.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,8 @@ Picking an existing keyword allows for this syntax, including extensions to othe
524524

525525
By not putting the generic parameters on `impl<..>`, we reduce the risk of confusion that we are somehow introducing generic parameters here rather than using them.
526526

527+
We put `impl` before `use<..>` because `use<..>` is a property of the opaque type and we're *applying* the generic *parameters* as generic *arguments* to this opaque type. In `impl Trait` syntax, the `impl` keyword is the stand-in for the opaque type itself. Viewed this way, `impl use<..> Trait` maintains the following order, which is seen throughout Rust: *type*, *generic arguments*, *bounds*.
528+
527529
Using angle brackets, rather than parenthesis or square brackets, is consistent with other places in the language where type parameters are applied to a type.
528530

529531
At three letters, the `use` keyword is short enough that it doesn't feel too noisy or too much like a burden to use this, and it's parsimonious with other short keywords in Rust.
@@ -536,6 +538,36 @@ The original syntax proposal was `impl<..> Trait`. This has the benefit of bein
536538

537539
Decisive to some was that we may want this syntax to *scale* to other uses, most particularly to controlling the set of generic parameters and values that are captured by closure-like blocks. As we discuss in the future possibilities, it's easy to see how `use<..>` can scale to address this in a way that `impl<..> Trait` cannot.
538540

541+
### `use<..> impl Trait`
542+
543+
Putting the `use<..>` specifier *before* the `impl` keyword is potentially appealing as `use<..>` applies to the entire `impl Trait` opaque type rather than to just one of the bounds, and this ordering might better suggest that.
544+
545+
However, this visual association might also *prove too much*. That is, it could make the `use<..>` look more like a *binder* (like `for<..>`) rather than like a *property* of the opaque type.
546+
547+
The `use<..>` syntax *applies* the listed generic *parameters* as generic *arguments* to the opaque type. It's analogous, e.g., with the generic arguments here:
548+
549+
```rust
550+
impl Trait for () {
551+
type Opaque<'t, T> = Concrete<'t, T>
552+
// ^^^^^^^^|^^^^^
553+
// Type | Generic Arguments
554+
where Self: 'static;
555+
// ^^^^^^^^^^^^^
556+
// Bounds
557+
}
558+
```
559+
560+
Just as the above *applies* `<'t, T>` to `Concrete`, `use<..>` applies its arguments to the opaque type.
561+
562+
In the above example and throughout Rust, we observe the following order: *type*, *generic arguments* (applied to the type), *bounds*. In `impl Trait` syntax, the `impl` keyword is the stand-in for the opaque type itself. The `use<..>` specifier lists the generic arguments to be applied to that type. Then the bounds follow. Putting `use<..>` after `impl` is consistent with this rule, but the other way would be inconsistent.
563+
564+
This observation, that we're applying generic *arguments* to the opaque type and that the `impl` keyword is the stand-in for that type, is also a strong argument in favor of `impl<..> Trait` syntax. It's conceivable that we'll later, with more experience and consistently with [Stroustrup's Rule][], decide that we want to be more concise and adopt the `impl<..> Trait` syntax after all. One of the advantages of placing `use<..>` after `impl` is that there would be less visual and conceptual churn in later making that change.
565+
566+
Finally, there's one other practical advantage to placing `impl` before `use<..>`. If we were to do it the other way and place `use<..>` before `impl`, we would need to make a backward incompatible change to the `ty` macro matcher fragment specifier. This would require us to migrate this specifier according to our policy in [RFC 3531][]. This is something we could do, but it is a cost on us and on our users, and combined with the other good reasons that argue for `impl use<..> Trait` (or even `impl<..> Trait`), it doesn't seem a cost that's worth paying.
567+
568+
[RFC 3531]: https://github.com/rust-lang/rfcs/blob/master/text/3531-macro-fragment-policy.md
569+
[Stroustrup's Rule]: https://www.thefeedbackloop.xyz/stroustrups-rule-and-layering-over-time/
570+
539571
### `impl Trait & ..`
540572

541573
In some conceptions, the difference between `impl Trait + 'a + 'b` and `impl use<'a, 'b> Trait` is the difference between capturing the union of those lifetimes and capturing the intersection of them. This inspires syntax proposals such as `impl Trait & 't & T` or `impl Trait & ['t, T]` to express this intersection.

0 commit comments

Comments
 (0)