You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: text/3437-implementable-trait-alias.md
+79-27Lines changed: 79 additions & 27 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,7 @@ Extend `#![feature(trait_alias)]` to permit `impl` blocks for trait aliases with
9
9
10
10
# Motivation
11
11
12
-
Often, one desires to have a "weak" version of a trait, as well as a "strong" one providing additional guarantees. Subtrait relationships are commonly used for this, but they sometimes fall short—expecially when the "strong" version is expected to see more use, or was stabilized first.
12
+
Often, one desires to have a "weak" version of a trait, as well as a "strong" one providing additional guarantees. Subtrait relationships are commonly used for this, but they sometimes fall short—especially when the "strong" version is expected to see more use, or was stabilized first.
13
13
14
14
## Example: AFIT `Send` bound aliases
15
15
@@ -120,7 +120,7 @@ error[E0404]: expected trait, found trait alias `Frobber`
120
120
121
121
## Speculative example: GATification of `Iterator`
122
122
123
-
*This example relies on some language features that are currently pure speculation. Implementable trait aliases are potentially necessary to support this use-case, but not sufficent.*
123
+
*This example relies on some language features that are currently pure speculation. Implementable trait aliases are potentially necessary to support this use-case, but not sufficient.*
124
124
125
125
Ever since the GAT MVP was stabilized, there has been discussion about how to add `LendingIterator` to the standard library, without breaking existing uses of `Iterator`. The relationship between `LendingIterator` and `Iterator` is "weak"/"strong"—an `Iterator` is a `LendingIterator` with some extra guarantees about the `Item` associated type.
126
126
@@ -150,7 +150,7 @@ There has been some discussion about a variant of the `Future` trait with an `un
150
150
151
151
# Guide-level explanation
152
152
153
-
With `#![feature(trait_alias)]` (RFC #1733), one can define trait aliases, for use in bounds, trait objects, and `impl Trait`. This feature additionaly allows writing `impl` blocks for a subset of trait aliases.
153
+
With `#![feature(trait_alias)]` (RFC #1733), one can define trait aliases, for use in bounds, trait objects, and `impl Trait`. This feature additionally allows writing `impl` blocks for a subset of trait aliases.
154
154
155
155
Let's rewrite our AFIT example from before, in terms of this feature. Here's what it looks like now:
156
156
@@ -185,7 +185,7 @@ impl Frobber for MyType {
185
185
186
186
Joe's original code Just Works.
187
187
188
-
The rule of thumb is: if you can copy everything between the `=` and `;` of a trait alias, paste it between the `for` and `{` of a trait `impl` block, and the result is sytactically valid—then the trait alias is most likely implementable.
188
+
The rule of thumb is: if you can copy everything between the `=` and `;` of a trait alias, paste it between the `for` and `{` of a trait `impl` block, and the result is syntactically valid—then the trait alias is most likely implementable.
189
189
190
190
# Reference-level explanation
191
191
@@ -203,22 +203,9 @@ Implementable trait aliases must follow a more restrictive form:
203
203
204
204
For example, `trait Foo<T> = PartialEq<T> where Self: Sync;` is a valid implementable alias. The `=` must be followed by a single trait (or implementable trait alias), and then some number of where clauses. The trait's generic parameter list may contain associated type constraints (for example `trait IntIterator = Iterator<Item = u32>`).
205
205
206
-
There is another restriction that trait aliases must adhere to in order to be implementable: all generic parameters of the alias itself must be used as generic parameters of the alias's primary trait.
207
-
208
-
```rust
209
-
// Implementable
210
-
traitFoo<T> =PartialEq<T>;
211
-
212
-
// Not implementable
213
-
traitFoo<T> =Copy;
214
-
traitFoo<T> =CopywhereT:Send;
215
-
traitFoo<T> =Iterator<Item=T>;
216
-
traitFoo<T> =CopywhereSelf:PartialEq<T>;
217
-
```
218
-
219
206
## Usage in `impl` blocks
220
207
221
-
An impl block for a trait alias looks just like an impl block for the underlying trait. The alias's where clauses are treated as if they had been written out in the `impl` header.
208
+
An `impl` block for a trait alias looks just like an `impl` block for the underlying trait. The alias's where clauses are enforced as requirements that the `impl`ing type must meet—just like `where` clauses in trait declarations are treated.
//impl CopyIterator for Bar { /* ... */ } // ERROR: `Bar` is not `Send`
227
+
```
228
+
229
+
```rust
230
+
traitFoo {}
231
+
traitBar=FoowhereSelf:Send;
232
+
//impl<T> Bar for T {} // ERROR: Need to add `T: Send` bound
233
+
```
234
+
```rust
235
+
#![feature(trivial_bounds)]
236
+
traitFoo {}
237
+
traitBar=FoowhereString:Copy;
238
+
//impl Bar for () {} // ERROR: `String: Copy` not satisfied
241
239
```
242
240
243
241
Bounds on generic parameters are also enforced at the `impl` site.
@@ -268,6 +266,28 @@ impl IntIterator for Baz {
268
266
}
269
267
```
270
268
269
+
Such constraints can be inferred indirectly:
270
+
271
+
```rust
272
+
traitBar:Iterator<Item=i32> {}
273
+
pubtraitIntIterator=IteratorwhereSelf:Bar;
274
+
275
+
structBaz;
276
+
277
+
implBarforBaz {}
278
+
279
+
implIntIteratorforBaz {
280
+
// `IntIterator` requires `Bar`,
281
+
// which requires `Iterator<Item = i32>`,
282
+
// so `Item` must be `i32`
283
+
// and we don't need to specify it.
284
+
285
+
fnnext(&mutself) ->i32 {
286
+
-27
287
+
}
288
+
}
289
+
```
290
+
271
291
Alias `impl`s also allow omitting implied `#[refine]`s:
272
292
273
293
```rust
@@ -306,11 +326,13 @@ Trait aliases are `unsafe` to implement iff the underlying trait is marked `unsa
306
326
Implementable trait aliases can also be used with trait-qualified and fully-qualified method call syntax, as well as in paths more generally. When used this way, they are treated equivalently to the underlying primary trait, with the additional restriction that all `where` clauses and type parameter/associated type bounds must be satisfied.
307
327
308
328
```rust
329
+
usestd::array;
330
+
309
331
traitIntIter=Iterator<Item=u32> whereSelf:Clone;
310
332
311
333
letiter= [1_u32].into_iter();
312
334
let_:IntIter::Item=IntIter::next(&mutiter); // works
313
-
let_: <std::array::IntoIterasIntIter>::Item= <std::array::IntoIterasIntIter>::next(); // works
335
+
let_: <array::IntoIterasIntIter>::Item= <array::IntoIterasIntIter>::next(); // works
314
336
//IntIter::clone(&iter); // ERROR: trait `Iterator` has no method named `clone()`
315
337
letdyn_iter:&mutdynIterator<Item=u32> =&mutiter;
316
338
//IntIter::next(dyn_iter); // ERROR: `dyn Iterator<Item = u32>` does not implement `Clone`
@@ -331,9 +353,7 @@ let _: IntIter<Item = u32> = [1_u32].into_iter(); // `Item = u32` is redundant,
331
353
332
354
# Drawbacks
333
355
334
-
- The sytactic distance between implementable and non-implementable aliases is short, which might confuse users. In particular, the fact that `trait Foo = Bar + Send;` means something different than `trait Foo = Bar where Self: Send;` will likely be surprising to many.
335
-
- On the other hand, the rules mirror those of `impl` blocks, which Rust programmers already understand.
336
-
- Ideally, we would collect user feedback before stabilizing this feature.
356
+
- The syntactic distance between implementable and non-implementable aliases is short, which might confuse users. In particular, the fact that `trait Foo = Bar + Send;` means something different than `trait Foo = Bar where Self: Send;` will likely be surprising to many.
337
357
- Adds complexity to the language, which might surprise or confuse users.
338
358
- Many of the motivating use-cases involve language features that are not yet stable, or even merely speculative. More experience with those features might unearth better alternatives.
339
359
@@ -343,12 +363,43 @@ let _: IntIter<Item = u32> = [1_u32].into_iter(); // `Item = u32` is redundant,
343
363
- Better ergonomics compared to purely proc-macro based solutions.
344
364
- One alternative is to allow marker traits or auto traits to appear in `+` bounds of implementable aliases.
345
365
(For example, `trait Foo = Bar + Send;` could be made implementable).
346
-
- This may make the implementablility rules more intutive to some, as the distinction between `+ Send` and `where Self: Send` would no longer be present.
366
+
- This may make the implementablility rules more intuitive to some, as the distinction between `+ Send` and `where Self: Send` would no longer be present.
347
367
- However, it also might make the rules less intuitive, as the symmetry with `impl` blocks would be broken.
368
+
- Also, such a change might break the commutativity of `+`, or make it less obvious which trait is being implemented.
348
369
- Again, user feedback could help make this decision.
349
370
- Another option is to require an attribute on implementable aliases; e.g. `#[implementable] trait Foo = ...`. This would make the otherwise-subtle implementability rules more explicit, at the cost of cluttering user code and the attribute namespace.
371
+
- A previous version of this RFC required type parameters of implementable trait aliases to be used as type parameters of the alias's primary trait. This restriction was meant to avoid surprising errors:
372
+
373
+
```rust
374
+
traitFoo<T> =Copy;
375
+
376
+
#[derive(Clone)]
377
+
structMyType;
378
+
379
+
impl<T> Foo<T> forMyType {} // ERROR: `T`` is unconstrained
However, upon further discussion, I now lean toward allowing more flexibility, even at the risk of potential confusion.
350
401
351
-
## What about combining multiple primary traits, and their items, into one impl block?
402
+
## What about combining multiple primary traits, and their items, into one `impl` block?
352
403
353
404
It's possible to imagine an extension of this proposal, that allows trait aliases to be implementable even if they have multiple primary traits. For example:
354
405
@@ -385,7 +436,7 @@ trait B {
385
436
traitC=A+B;
386
437
```
387
438
388
-
Such a feature could also make it harder to find the declaration of a trait item from its implementation, especially if IDE "go to definition" is not available. One would need to first find the trait alias definition, and then look through every primary trait to find the item. (However, given the current situation with postfix method call syntax, maybe this is an acceptable tradeoff.)
439
+
Such a feature could also make it harder to find the declaration of a trait item from its implementation, especially if IDE "go to definition" is not available. One would need to first find the trait alias definition, and then look through every primary trait to find the item. (However, given the current situation with postfix method call syntax, maybe this is an acceptable trade-off.)
389
440
390
441
Perhaps a more narrowly tailored version of this extension, in which both subtrait and supertrait explicitly opt-in to support sharing an `impl` block with one another, would satisfy the backward-compatibility use-case while avoiding the above issues. I think exploring that is best left to a future RFC.
391
442
@@ -403,4 +454,5 @@ Perhaps a more narrowly tailored version of this extension, in which both subtra
403
454
- Variance bounds would allow this feature to support backward-compatible GATification.
404
455
- Method unsafety bounds would support the `Future` → `Async` use-case.
405
456
-`trait Foo: Copy = Iterator;` could be allowed as an alternative to `trait Foo = Iterator where Self: Copy;`.
457
+
-`impl Trait<Assoc = Ty> for Type { /* ... */ }` could be permitted in the future, to make the "copy-paste" rule of thumb work better.
406
458
- The possible contents of `impl` bodies could be expanded, for example to support combining supertrait and subtrait implementations.
0 commit comments