Skip to content

Commit

Permalink
Merge pull request rust-lang#3 from Veykril/arb-self-ty
Browse files Browse the repository at this point in the history
Dump my notes on arbitrary self types
  • Loading branch information
adetaylor authored May 25, 2023
2 parents 3472500 + e11383b commit a4bedb6
Showing 1 changed file with 101 additions and 17 deletions.
118 changes: 101 additions & 17 deletions text/0000-stabilize-arbitrary-self-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ See also [this blog post](https://medium.com/@adetaylor/the-case-for-stabilizing
# Guide-level explanation
[guide-level-explanation]: #guide-level-explanation

When declaring a method, users can declare the type of the `self` receiver to be any type `T` where `T: Receiver<Target = Self>`.
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`.

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

Expand All @@ -39,7 +39,7 @@ trait Receiver {

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

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`.
Expand All @@ -65,7 +65,7 @@ impl<T> Receiver for CustomPtr<T> {

## Recursive arbitrary receivers

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).
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).

For example, this self type is valid:

Expand All @@ -75,17 +75,39 @@ impl MyType {
}
```

## Object safety

TODO:
- DispatchFromDyn
- What is the relation with Lukas' [RFC "Unsize and CoerceUnsize v2"](https://github.com/ferrous-systems/unsize-experiments)?

# Reference-level explanation
[reference-level-explanation]: #reference-level-explanation

The `Receiver` trait is made public (removing its `#[doc(hidden)])` attribute), exposing it under `core::ops`.
This trait marks types that can be used as receivers other than the `Self` type of an impl or trait definition.

```rust
pub trait Receiver {
type Target: ?Sized;
}
```

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.

The receiver chain is constructed as follows:
1. Add the receiver type `T` to the chain.
2. If `T` implements `Receiver`, add `<T as Receiver>::Target` to the chain.
3. Repeat step 2 with the `<T as Receiver>::Target` as `T`.

## Method Resolution

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.

## core lib changes

The implementations of the hidden `Receiver` trait will be adjusted by removing the `#[doc(hidden)]` attribute and having their corresponding `Target` associated type added.

## Lifetime Elision

TODO

## Diagnostics

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

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

Why should we *not* do this?

- Implementations of this trait may obscure what method is being called where in similar ways to deref coercions obscuring it.
- The use of this feature together with `Deref` implementations may cause ambiguious situations,
```rs

pub struct Ptr<T>(T);

impl<T> Deref<T> {
type Target = T;

fn deref(&self) -> &T {
&self.0
}
}

impl<T> Ptr<T> {
pub fn foo(&self) {}
}

pub struct Bar;

impl Bar {
fn foo(self: &Ptr<Self>) {}
}
```
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.

## Object safety

Receivers remain object safe as before, if they implement the `core::ops::DispatchFromDyn` trait they are object safe.
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.


TODO. To include:

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


# Rationale and alternatives
[rationale-and-alternatives]: #rationale-and-alternatives

- 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.
- Change the trait definition to
```rust
pub trait Receiver<T: ?Sized> {}
```
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.


TODO:

- Why is this design the best in the space of possible designs?
- What other designs have been considered and what is the rationale for not choosing them?
- What is the impact of not doing this?
- 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?

TODO. To include:
To include:

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

A previous PR based on the `Deref` alternative has been proposed before https://github.com/rust-lang/rfcs/pull/2362.

TODO:

Discuss prior art, both the good and the bad, in relation to this proposal.
A few examples of what this can include are:

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

TODO

# Unresolved questions
[unresolved-questions]: #unresolved-questions

- There is the option of doing a blanket impl of the `Receiver` trait based on `Deref` impls delegating the `Target` types.

- With the proposed design, it is not possible to be generic over the receiver while permitting the plain `Self` to be slotted in:
```rs
use std::ops::Receiver;

struct Foo(u32);
impl Foo {
fn get<R: Receiver<Target=Self>>(self: R) -> u32 {
self.0
}
}

fn main() {
let mut foo = Foo(1);
foo.get::<&Foo>(); // Error
}
```
This fails, because `T: Receiver<Target=T>` generally does not hold.
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.

TODO:

- What parts of the design do you expect to resolve through the RFC process before this gets merged?
- What parts of the design do you expect to resolve through the implementation of this feature before stabilization?
- 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?

TODO

# Future possibilities
[future-possibilities]: #future-possibilities

Expand Down

0 comments on commit a4bedb6

Please sign in to comment.