Skip to content

Commit 4879d57

Browse files
Address comments
- Add section on `dyn` - Discuss `Deref` example - Various minor fixes
1 parent e5c19f0 commit 4879d57

File tree

1 file changed

+115
-19
lines changed

1 file changed

+115
-19
lines changed

text/3437-implementable-trait-alias.md

Lines changed: 115 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -178,23 +178,26 @@ With today's `trait_alias`, it wouldn't make much difference for `downstream`.
178178

179179
## Splitting a trait
180180

181-
Consider this humble library:
181+
To exemplify this use-case, we will use [Niko Matsakis’s proposal to split the
182+
`Deref`
183+
trait](https://github.com/rust-lang/rust/pull/135881#issuecomment-2718417230).
184+
185+
`Deref` currently looks like this:
182186

183187
```rust
184-
trait Frob {
185-
fn frazzle(&self);
186-
fn brop(&self);
188+
pub trait Deref {
189+
type Target: ?Sized;
190+
191+
fn deref(&self) -> &Self::Target;
187192
}
188193
```
189194

190-
Now, let us imagine that the authors of `frob-lib` realize that some types can
191-
only implement one of `frazzle` or `brop`, but not both. They want to split the
192-
trait in two. But, as with our previous example, there is no way to do this in a
193-
backward-compatible way without painful compromises.
195+
Niko wants to split off the `type Target` part into a separate `Receiver`
196+
supertrait. But there is no backward-compatible way to do this at present.
194197

195198
## Removing a `Sized` bound
196199

197-
Consider this other humble library:
200+
Consider this humble library:
198201

199202
```rust
200203
trait Frob {
@@ -205,8 +208,8 @@ trait Frob {
205208
```
206209

207210
Currently, `Frob::Frobber` has a `Sized` bound, but the signature of `frob()`
208-
doesn't require it. However, there is no good way at present for `frob-lib` to
209-
remove the bound.
211+
doesn't require it. However, there is no backward-compatible way at present for
212+
`frob-lib` to remove the bound.
210213

211214
## Renaming trait items
212215

@@ -353,6 +356,22 @@ impls for the two underlying traits. Or, alternatively, you can give the trait
353356
alias a body, and define item aliases with distinct names for each of the
354357
conflicting items.
355358

359+
We can use this to split the `Deref` trait, as suggested in the motivation section:
360+
361+
```rust
362+
//! New `Deref`
363+
364+
pub trait Reciever {
365+
type Target: ?Sized;
366+
}
367+
368+
pub trait DerefToTarget: Reciever {
369+
fn deref(&self) -> &Self::Target;
370+
}
371+
372+
pub trait Deref = Receiver + DerefToTarget;
373+
```
374+
356375
# Reference-level explanation
357376

358377
## Implementing trait aliases
@@ -418,8 +437,8 @@ impl IntIterator for Baz {
418437
// The alias constrains `Self::Item` to `i32`, so we don't need to specify it
419438
// (though we are allowed to do so if desired).
420439

421-
fn next(&mut self) -> i32 {
422-
-27
440+
fn next(&mut self) -> Option<i32> {
441+
Some(-27)
423442
}
424443
}
425444
```
@@ -440,8 +459,8 @@ impl IntIterator for Baz {
440459
// so `Item` must be `i32`
441460
// and we don't need to specify it.
442461

443-
fn next(&mut self) -> i32 {
444-
-27
462+
fn next(&mut self) -> Option<i32> {
463+
Some(-27)
445464
}
446465
}
447466
```
@@ -494,7 +513,7 @@ trait IntIter = Iterator<Item = u32> where Self: Clone;
494513
let iter = [1_u32].into_iter();
495514
let _: IntIter::Item = IntIter::next(&mut iter); // works
496515
let _: <array::IntoIter as IntIter>::Item = <array::IntoIter as IntIter>::next(); // works
497-
//IntIter::clone(&iter); // ERROR: trait `Iterator` has no method named `clone()`
516+
IntIter::clone(&iter);
498517
let dyn_iter: &mut dyn Iterator<Item = u32> = &mut iter;
499518
//IntIter::next(dyn_iter); // ERROR: `dyn Iterator<Item = u32>` does not implement `Clone`
500519
let signed_iter = [1_i32].into_iter();
@@ -763,6 +782,64 @@ This is similar to defining an extension trait like
763782
(One difference from extension traits is that trait aliases do not create their
764783
own `dyn` types.)
765784

785+
## Interaction with `dyn`
786+
787+
Trait aliases do not define their own `dyn` types. This RFC does not change that
788+
pre-existing behavior. However, we do make one change to which trait aliases
789+
also define a type alias for a trait object. If a trait alias contains multiple
790+
non-auto traits (primary or not), but one of them is a subtrait of all the
791+
others, then the corresponding `dyn` type for that trait alias is now an alias
792+
for the `dyn` type for that subtrait.
793+
794+
This is necessary to support the `Deref` example from earlier.
795+
796+
```rust
797+
trait Foo {
798+
fn foo(&self);
799+
}
800+
trait Bar: Foo {
801+
fn bar(&self);
802+
}
803+
804+
trait FooBar = Foo + Bar; // `dyn FooBar` is an alias of `dyn Bar`
805+
trait FooBar2 = Foo
806+
where
807+
Self: Bar; // `dyn FooBar2` is also an alias of `dyn Bar`
808+
```
809+
810+
N.B.: when using implementable trait aliases to split a trait into two parts
811+
*without* a supertrait/subtrait relationship between them, you have to be
812+
careful in order to preserve `dyn` compatiblilty.
813+
814+
```rust
815+
trait Foo {
816+
fn foo(&self);
817+
}
818+
trait Bar {
819+
fn bar(&self);
820+
}
821+
822+
trait FooBar = Foo + Bar; // `dyn FooBar` is not a valid type!
823+
```
824+
825+
To make it work, you can do:
826+
827+
```rust
828+
trait Foo {
829+
fn foo(&self);
830+
}
831+
832+
trait Bar {
833+
fn bar(&self);
834+
}
835+
836+
#[doc(hidden)]
837+
trait FooBarDyn: Foo + Bar {}
838+
impl<T: Foo + Bar + ?Sized> FooBarDyn for T {}
839+
840+
trait FooBar = Foo + Bar + FooBarDyn; // `dyn FooBar` now works just fine
841+
```
842+
766843
# Drawbacks
767844

768845
- The fact that `trait Foo = Bar + Send;` means something different than `trait
@@ -786,6 +863,10 @@ complexity of the feature. However, it would also reduce its power: trait
786863
aliases could no longer be used to rename trait items, and naming conflicts in
787864
multi-primary-trait aliases would be impossible to resolve.
788865

866+
It's this last issue especially that leads me to not relegate this to a future
867+
possibility. Adding a defaulted item to a trait should at most require minor
868+
changes to dependents, and restructuring a large `impl` block is not “minor”.
869+
789870
## No non-implementable items in trait alias bodies
790871

791872
Such items don't have much utility from a backward-compatibility perspective,
@@ -830,7 +911,8 @@ even at the risk of potential confusion.
830911

831912
## Allow `impl Foo + Bar for Type { ... }` directly, without an alias
832913

833-
It's a forward-compatibility hazard, with no use-case that I can see.
914+
It's a forward-compatibility hazard (if the traits gain items with conflicting
915+
names), with no use-case that I can see.
834916

835917
## Implementing aliases with 0 primary traits
836918

@@ -851,9 +933,23 @@ I don't see the point in it.
851933
make this feature more powerful as well.
852934
- Variance bounds could allow this feature to support backward-compatible
853935
GATification.
854-
- `trait Foo: Copy = Iterator;` could be allowed as an alternative syntax to
855-
`trait Foo = Iterator where Self: Copy;`.
856936
- We could allow trait aliases to define their own defaults for `impl`s. One
857937
possibility is [the `default partial impl` syntax I suggested on
858938
IRLO](https://internals.rust-lang.org/t/idea-partial-impls/22706/).
859939
- We could allow implementable `fn` aliases in non-alias `trait` definitions.
940+
- We could allow any `impl` block to implement items from supertraits of its
941+
primary trait(s).
942+
- This would allow splitting a trait into a supertrait and a subtrait without
943+
having to give the subtrait a new name.
944+
- However, it would make it more difficult to deduce what traits an `impl`
945+
block is implementing.
946+
- In addition, it poses a danger if an `unsafe` subtrait depends on an
947+
`unsafe` marker supertrait: you could implement the subtrait, carefully
948+
checking that you meet its preconditions, while not realizing that you are
949+
also implementing the supertrait and need to check its conditions as well.
950+
- And even if the traits are not `unsafe`, they could still have preconditions
951+
that are important for correctness. Users should never be committing to such
952+
things unknowingly.
953+
- We could add an attribute for trait aliases to opt in to generating their own
954+
`dyn` type.
955+
- This could be prototyped as a proc macro.

0 commit comments

Comments
 (0)