Skip to content

Commit a4bedb6

Browse files
authored
Merge pull request rust-lang#3 from Veykril/arb-self-ty
Dump my notes on arbitrary self types
2 parents 3472500 + e11383b commit a4bedb6

File tree

1 file changed

+101
-17
lines changed

1 file changed

+101
-17
lines changed

text/0000-stabilize-arbitrary-self-types.md

+101-17
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ See also [this blog post](https://medium.com/@adetaylor/the-case-for-stabilizing
2727
# Guide-level explanation
2828
[guide-level-explanation]: #guide-level-explanation
2929

30-
When declaring a method, users can declare the type of the `self` receiver to be any type `T` where `T: Receiver<Target = Self>`.
30+
When declaring a method, users can declare the type of the `self` receiver to be any type `T` where `T: Receiver<Target = Self>` or `Self`.
3131

3232
The `Receiver` trait is simple and only requires to specify the `Target` type to be resolved to:
3333

@@ -39,7 +39,7 @@ trait Receiver {
3939

4040
The `Receiver` trait is already implemented for a few types from the standard, i.e.
4141
- smart pointer: `Arc<Self>`, `Box<Self>`, `Pin<Self>` and `Rc<Self>`
42-
- references: `&Self`, `&mut Self` and `Self`
42+
- references: `&Self` and `&mut Self`
4343
- raw pointer: `*const Self` and `*mut Self`
4444

4545
Shorthand exists for references, so that `self` with no ascription is of type `Self`, `&self` is of type `&Self` and `&mut self` is of type `&mut Self`.
@@ -65,7 +65,7 @@ impl<T> Receiver for CustomPtr<T> {
6565

6666
## Recursive arbitrary receivers
6767

68-
Receivers are recursive and therefore allowed to be nested. If type `T` implements `Receiver<Target=U>`, and type `U` implements `Deref<Target=Self>`, `T` is a valid receiver (and so on outward).
68+
Receivers are recursive and therefore allowed to be nested. If type `T` implements `Receiver<Target=U>`, and type `U` implements `Receiver<Target=Self>`, `T` is a valid receiver (and so on outward).
6969

7070
For example, this self type is valid:
7171

@@ -75,17 +75,39 @@ impl MyType {
7575
}
7676
```
7777

78-
## Object safety
79-
80-
TODO:
81-
- DispatchFromDyn
82-
- What is the relation with Lukas' [RFC "Unsize and CoerceUnsize v2"](https://github.com/ferrous-systems/unsize-experiments)?
83-
8478
# Reference-level explanation
8579
[reference-level-explanation]: #reference-level-explanation
8680

81+
The `Receiver` trait is made public (removing its `#[doc(hidden)])` attribute), exposing it under `core::ops`.
82+
This trait marks types that can be used as receivers other than the `Self` type of an impl or trait definition.
83+
84+
```rust
85+
pub trait Receiver {
86+
type Target: ?Sized;
87+
}
88+
```
89+
90+
For a type to be a valid receiver for a given impl, the `Self` type of the impl or trait has to appear in its receiver chain.
91+
92+
The receiver chain is constructed as follows:
93+
1. Add the receiver type `T` to the chain.
94+
2. If `T` implements `Receiver`, add `<T as Receiver>::Target` to the chain.
95+
3. Repeat step 2 with the `<T as Receiver>::Target` as `T`.
96+
97+
## Method Resolution
98+
99+
Method resolution will be adjusted to take into account a receiver's receiver chain by taking looking through all impls for all types appearing in the receiver chain.
100+
101+
## core lib changes
102+
103+
The implementations of the hidden `Receiver` trait will be adjusted by removing the `#[doc(hidden)]` attribute and having their corresponding `Target` associated type added.
104+
105+
## Lifetime Elision
106+
87107
TODO
88108

109+
## Diagnostics
110+
89111
TODO ensure we include some analysis of extra diagnostics required. Known gotchas:
90112
- In a trait, using `self: SomeSmartPointerWhichOnlySupportsSizedTypes<Self>` without using `where Self: Sized` on the trait definition results in poor diagnostics.
91113

@@ -102,25 +124,64 @@ The section should return to the examples given in the previous section, and exp
102124

103125
Why should we *not* do this?
104126

127+
- Implementations of this trait may obscure what method is being called where in similar ways to deref coercions obscuring it.
128+
- The use of this feature together with `Deref` implementations may cause ambiguious situations,
129+
```rs
130+
131+
pub struct Ptr<T>(T);
132+
133+
impl<T> Deref<T> {
134+
type Target = T;
135+
136+
fn deref(&self) -> &T {
137+
&self.0
138+
}
139+
}
140+
141+
impl<T> Ptr<T> {
142+
pub fn foo(&self) {}
143+
}
144+
145+
pub struct Bar;
146+
147+
impl Bar {
148+
fn foo(self: &Ptr<Self>) {}
149+
}
150+
```
151+
invoking `Ptr(Bar).foo()` here will require the use of fully qualified paths (`Bar::foo` vs `Ptr::foo` or `Ptr::<T>::foo`) to disambiguate the call.
152+
153+
## Object safety
154+
155+
Receivers remain object safe as before, if they implement the `core::ops::DispatchFromDyn` trait they are object safe.
156+
As not all receivers might want to permit object safety (or are even unable to support it), object safety should remain being encoded in a different trait than the here proposed `Receiver` trait.
157+
158+
105159
TODO. To include:
106160

107-
- object safety
108161
- compatibility concerns / does this break semver?
109-
- does it cause any risk that folks implementing `Deref` for other reasons will also be forced into new behavior? ("scoping")
110162
- method shadowing
111-
- if we later implement `MethodReceiver` to allow
112163
- all the other concerns discussed in https://github.com/rust-lang/rust/issues/44874#issuecomment-1306142542
113164

114165

115166
# Rationale and alternatives
116167
[rationale-and-alternatives]: #rationale-and-alternatives
117168

169+
- An alternative proposal is to use `Deref` and it's associated `Target` type instead. This comes with the problem that not all types that wish to be receivers are able to implement `Deref`, a simple example being raw pointers.
170+
- Change the trait definition to
171+
```rust
172+
pub trait Receiver<T: ?Sized> {}
173+
```
174+
to allow an impl of the form `impl Receiver<T> for T` which would enable `Self` to be used as is by the trait impl rule instead of a special case.
175+
176+
177+
TODO:
178+
118179
- Why is this design the best in the space of possible designs?
119180
- What other designs have been considered and what is the rationale for not choosing them?
120181
- What is the impact of not doing this?
121182
- If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain?
122183

123-
TODO. To include:
184+
To include:
124185

125186
- Arguments for/against doing this as a new `MethodReceiver` trait
126187
- Arguments for/against stabilizing the unstable `Receiver` trait instead of this
@@ -130,6 +191,10 @@ TODO. To include:
130191
# Prior art
131192
[prior-art]: #prior-art
132193

194+
A previous PR based on the `Deref` alternative has been proposed before https://github.com/rust-lang/rfcs/pull/2362.
195+
196+
TODO:
197+
133198
Discuss prior art, both the good and the bad, in relation to this proposal.
134199
A few examples of what this can include are:
135200

@@ -144,17 +209,36 @@ If there is no prior art, that is fine - your ideas are interesting to us whethe
144209
Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC.
145210
Please also take into consideration that rust sometimes intentionally diverges from common language features.
146211

147-
TODO
148-
149212
# Unresolved questions
150213
[unresolved-questions]: #unresolved-questions
151214

215+
- There is the option of doing a blanket impl of the `Receiver` trait based on `Deref` impls delegating the `Target` types.
216+
217+
- With the proposed design, it is not possible to be generic over the receiver while permitting the plain `Self` to be slotted in:
218+
```rs
219+
use std::ops::Receiver;
220+
221+
struct Foo(u32);
222+
impl Foo {
223+
fn get<R: Receiver<Target=Self>>(self: R) -> u32 {
224+
self.0
225+
}
226+
}
227+
228+
fn main() {
229+
let mut foo = Foo(1);
230+
foo.get::<&Foo>(); // Error
231+
}
232+
```
233+
This fails, because `T: Receiver<Target=T>` generally does not hold.
234+
An alternative would be to lift the associated type into a generic type parameter of the `Receiver` trait, that would allow adding a blanket `impl Receiver<T> for T` without overlap.
235+
236+
TODO:
237+
152238
- What parts of the design do you expect to resolve through the RFC process before this gets merged?
153239
- What parts of the design do you expect to resolve through the implementation of this feature before stabilization?
154240
- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC?
155241

156-
TODO
157-
158242
# Future possibilities
159243
[future-possibilities]: #future-possibilities
160244

0 commit comments

Comments
 (0)