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
This changes the RFC in two significant ways:
* As requested widely, it now proposes that we implement Receiver for
NonNull<T> and Weak<T>. This requires us, for the first time, to add explicit
code to spot potentially shadowed methods and avoid such shadowing. This is
described.
* It expands the Reference section to describe changes to the probing algorithm,
which are now a little more extensive than the previous version of the RFC
described, because we now search two different chains - one for types into
which the receiver can be converted, and another chain for locations to search
for possible methods.
Copy file name to clipboardExpand all lines: text/3519-arbitrary-self-types-v2.md
+79-10Lines changed: 79 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -228,15 +228,46 @@ where
228
228
229
229
(See [alternatives](#no-blanket-implementation) for discussion of the tradeoffs here.)
230
230
231
-
It is also implemented for `&T`, `&mut T`, `*const T` and `*mut T`.
231
+
It is also implemented for `&T`, `&mut T`, `Weak<T>`, `NonNull<T>`, `*const T` and `*mut T`.
232
232
233
-
## Compiler changes
233
+
## Compiler changes: method probing
234
234
235
-
The existing Rust [reference section for method calls describes the algorithm for assembling method call candidates](https://doc.rust-lang.org/reference/expressions/method-call-expr.html). This algorithm changes in one simple way: instead of dereferencing types (using the `Deref<Target=T>`) trait, we use the new `Receiver<Target=T>` trait to determine the next step.
235
+
The existing Rust [reference section for method calls describes the algorithm for assembling method call candidates](https://doc.rust-lang.org/reference/expressions/method-call-expr.html), and there's more detail in the [rustc dev guide](https://rustc-dev-guide.rust-lang.org/method-lookup.html).
236
236
237
-
(Note that the existing algorithm isn't quite as simple as following the chain of `Deref<Target=T>`. In particular, `&T` and `&mut T` are considered as candidates too at each step; this RFC does not change that.)
237
+
The key part of the first page is this:
238
238
239
-
Because a blanket implementation is provided for users of the `Deref` trait and for `&T`/`&mut T`, the net behavior is similar. But this provides the opportunity for types which can't implement `Deref` to act as method receivers.
239
+
> Then, for each candidate type `T`, search for a visible method with a receiver of that type in the following places:
240
+
> -`T`'s inherent methods (methods implemented directly on `T`).
241
+
> Any of the methods provided by a visible trait implemented by `T`.
242
+
243
+
This changes.
244
+
245
+
The list of candidate types is assembled in exactly the same way, but we now search for a visible method with a receiver of that type in _more_ places.
246
+
247
+
Specifically, instead of using the list of candidate types assembled using the `Deref` trait, we search a list assembled using the `Receiver` trait. As `Receiver` is implemented for all types that implement `Deref`, this is a longer list.
248
+
249
+
It's particularly important to emphasize that the list of candidate receiver types _does not change_ - that's still assembled using the `Deref` trait just as now. But, a wider set of locations is searched for methods with those receiver types.
250
+
251
+
For instance, `Weak<T>` implements `Receiver` but not `Deref`. Imagine you have `let t: Weak<SomeStruct> = /* obtain */; t.some_method();`. We will now search `impl SomeStruct {}` blocks for an implementation of `fn some_method(self: Weak<SomeStruct>)`, `fn some_method(self: &Weak<SomeStruct>)`, etc. The possible self types are unchanged - they're still obtained by searching the `Deref` chain for `t` - but we'll look in more places for methods with those valid `self` types.
The major functional change to the compiler is described above, but a couple of extra adjustments are necessary to avoid future compatibility breaks by method shadowing.
257
+
258
+
Specifically, that page also states:
259
+
260
+
> If this results in multiple possible candidates, then it is an error, and the receiver must be converted to an appropriate receiver type to make the method call.
261
+
262
+
This changes. For smart pointer types which implement `Receiver` (such as `NonNull<T>`) the future addition of any method would become an incompatible change, because it would run the risk of this ambiguity if there were a method of the same name within `T`. So, if there are multiple candidates, and if one of those candidates is in a more "nested" level of receiver than the others (that is, further along the chain of `Receiver`), we will choose that candidate and warn instead of producing a fatal error.
263
+
264
+
Similarly,
265
+
266
+
> Note: the lookup is done for each type in order, which can occasionally lead to surprising results.
267
+
268
+
This changes too, for the same reason. We check for matching candidates for `T`, `&T` and `&mut T`, and again, if there's a candidate on an "inner" type (that, is, further along the chain of `Receiver`) we will choose that type in preference to less nested types and emit a warning.
269
+
270
+
(The current reference doesn't describe it, but the current algorithm also searches for method receivers of type `*const Self` and handles them explicitly in case the receiver type was `*mut Self`. We do not check for cases where a new `self: *mut Self` method on an outer type might shadow an existing `self: *const SomePtr<Self>` method on an inner type. Although this is a theoretical risk, such compatibility breaks should be easy to avoid because `self: *mut Self` are rare. It's not readily possible to apply the same de-shadowing approach to these, because we already intentionally shadow `*const::cast` with `*mut::cast`.)
240
271
241
272
## Object safety
242
273
@@ -276,6 +307,11 @@ The existing branches in the compiler for "arbitrary self types" already emit ex
276
307
}
277
308
```
278
309
We don't know a use-case for this. There are several cases where this can result in misleading diagnostics. (For instance, if such a method is called with an incorrect type (for example `smart_ptr.a::<&Foo>()` instead of `smart_ptr.a::<Foo>()`). We could attempt to find and fix all those cases. However, we feel that generic receiver types might risk subtle interactions with method resolutions and other parts of the language. We think it is a safer choice to generate an error on any declaration of a generic `self` type.
310
+
- As noted in [#compiler-changes-deshadowing](the section about compiler changes for deshadowing) we will downgrade an existing error to a warning if there are multiple
311
+
method candidates found, if one of those candidates is further along the chain of `Receiver`s than the others.
312
+
- As also noted in [#compiler-changes-deshadowing](the section about compiler changes for deshadowing), we will produce a new warning if a method in an inner type is chosen
313
+
in preference to a method in an outer type ("inner" = further along the `Receiver` chain) and the inner type is either `self: &T` or `self: &mut T` and we're choosing it
314
+
in preference to `self: T` or `self: &T` in the outer type.
279
315
280
316
# Drawbacks
281
317
[drawbacks]: #drawbacks
@@ -297,7 +333,44 @@ Rust standard library smart pointers are designed with this shadowing behavior i
297
333
298
334
Furthermore, the `Deref` trait itself [documents this possible compatibility hazard](https://doc.rust-lang.org/nightly/std/ops/trait.Deref.html#when-to-implement-deref-or-derefmut), and the Rust API Guidelines has [a guideline about avoiding inherent methods on smart pointers](https://rust-lang.github.io/api-guidelines/predictability.html#smart-pointers-do-not-add-inherent-methods-c-smart-ptr).
299
335
300
-
These method shadowing risks also apply to `Receiver`. This RFC does not make things worse for types that implement `Deref`, it only adds additional flexibility to the `self` parameter type for `T::m`.
336
+
This RFC does not make things worse for types that implement `Deref`.
337
+
338
+
_However_, this RFC allow types to implement `Receiver`, and in fact does so for `NonNull<T>` and `Weak<T>`. `NonNull<T>` and `Weak<T>` were not designed with method shadowing concerns in mind. This would run the risk of breakage:
339
+
340
+
```rust
341
+
structConcrete;
342
+
343
+
implConcrete {
344
+
fnwardrobe(self:Weak<Self>) { }
345
+
}
346
+
347
+
fnmain() {
348
+
letconcrete:Weak<Concrete> =/* obtain */;
349
+
concrete.wardrobe()
350
+
}
351
+
```
352
+
353
+
If Rust now adds `Weak::wardrobe(self)`, the above valid code would start to error.
354
+
355
+
The same would apply in this slightly different circumstance:
356
+
357
+
```rust
358
+
structConcrete;
359
+
360
+
implConcrete {
361
+
fnwardrobe(self:&Weak<Self>) { } // this is now a reference
362
+
}
363
+
364
+
fnmain() {
365
+
letconcrete:Weak<Concrete> =/* obtain */;
366
+
concrete.wardrobe()
367
+
}
368
+
```
369
+
370
+
If Rust added `Weak::wardrobe(&self)` we would start to produce an error here. If Rust added `Weak::wardrobe(self)` then it would be
371
+
even worse - code would start to call `Weak::wardrobe` where it had previously called `Concrete::wardrobe`.
372
+
373
+
The [#compiler-changes-deshadowing](deshadowing section of the compiler changes, above), describes how we avoid this. The compiler will take pains to identify any such ambiguities. If it finds them, it will warn of the situation and then choose the innermost method (in the example above, always `Concrete::wardrobe`).
@@ -367,10 +440,6 @@ This RFC proposes to implement `Receiver` for `*mut T` and `*const T` within the
367
440
368
441
We prefer the option of specifying behavior in the library using the normal trait, though it's a compatibility break for users of Rust who don't adopt the `core` crate (including compiler tests).
369
442
370
-
## Implement for `Weak` and `NonNull`
371
-
372
-
`Weak<T>` and `NonNull<T>` were not supported by the prior unstable arbitrary self types support, but they share the property that it may be desirable to implement method calls to `T` using them as self types. Unfortunately they also share the property that these types have many Rust methods using `self`, `&self` or `&mut self`. If we added to the set of Rust methods in future, we'd [shadow any such method calls](#method-shadowing). We can't implement `Receiver` for these types unless we come up with a policy that all subsequent additions to these types would instead be associated functions. That would make the future APIs for these types a confusing mismash of methods and associated functions, and the extra user complexity doesn't seem merited.
0 commit comments