Tracking issue for RFC 3519: arbitrary_self_types
This is the tracking issue for RFC 3519: Arbitrary self types v2.
The feature gate for this issue is #![feature(arbitrary_self_types)].
About tracking issues
Tracking issues are used to record the overall progress of implementation. They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
- [x] Accept RFC.
- https://github.com/rust-lang/rfcs/pull/3519
- [ ] Implement.
- [ ] Add documentation to the dev guide.
- See the instructions.
- [ ] Add documentation to the reference.
- See the instructions.
- [ ] Add formatting for new syntax to the style guide.
- See the nightly style procedure.
Unresolved Questions
None.
Related
- #27941
Implementation history
TODO.
(Below follows content that predated the accepted Arbitrary Self Types v2 RFC.)
- [ ] figure out the object safety situation
- [ ] figure out the handling of inference variables behind raw pointers
- [ ] decide whether we want safe virtual raw pointer methods
Object Safety
See https://github.com/rust-lang/rust/issues/27941#issuecomment-332157526
Handling of inference variables
Calling a method on *const _ could now pick impls of the form
impl RandomType {
fn foo(*const Self) {}
}
Because method dispatch wants to be "limited", this won't really work, and as with the existing situation on &_ we should be emitting an "the type of this value must be known in this context" error.
This feels like fairly standard inference breakage, but we need to check the impact of this before proceeding.
Safe virtual raw pointer methods
e.g. this is UB, so we might want to force the call <dyn Foo as Foo>::bar to be unsafe somehow - e.g. by not allowing dyn Foo to be object safe unless bar was an unsafe fn
trait Foo {
fn bar(self: *const Self);
}
fn main() {
// creates a raw pointer with a garbage vtable
let foo: *const dyn Foo = unsafe { mem::transmute([0usize, 0x1000usize]) };
// and call it
foo.bar(); // this is UB
}
However, even today you could UB in safe code with mem::size_of_val(foo) on the above code, so this might not be actually a problem.
More information
There's no reason the self syntax has to be restricted to &T, &mut T and Box<T>, we should allow for more types there, e.g.
trait MyStuff {
fn do_async_task(self: Rc<Self>);
}
impl MyStuff for () {
fn do_async_task(self: Rc<Self>) {
// ...
}
}
Rc::new(()).do_async_stuff();
This is the tracking issue for RFC 3519: Arbitrary self types v2.
The feature gate for this issue is
#![feature(arbitrary_self_types)].About tracking issues
Tracking issues are used to record the overall progress of implementation. They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
Current plan is:
- [x] Fix the current lifetime elision bugs
- [x] Search for potentially conflicting method candidates
- [x] Block generic arbitrary self types.
- [x] Introduce an
arbitrary_self_types_pointersfeature gate - [x] Rename the old
Receivertrait -- currently being run through crater - [x] Land the new
Receivertrait without it doing anything. - [x] Switch over the method resolution to use the
Receivertrait, if thearbitrary_self_typesfeature is enabled. The main event. - [ ] Add diagnostics for the
!Sizedcase and theNonNulletc. cases. - [ ] Update the Rust reference.
- https://github.com/rust-lang/reference/pull/1699
- https://github.com/rust-lang/reference/pull/1725
- [ ] Add documentation to the dev guide. See the instructions.
- [ ] Add formatting for new syntax to the style guide. See the nightly style procedure.
- [ ] Clear out stabilization blockers.
- https://github.com/rust-lang/rust/pull/136764
- https://github.com/rust-lang/rust/pull/136776
- [ ] Stabilize!
- https://github.com/rust-lang/rust/pull/135881
Unresolved Questions
None.
Notable for Stabilization
- https://github.com/rust-lang/rust/pull/134524
Related
- #27941
Implementation history
- #117967
- #129664
- #130098
- #130225
- #132144
- #132961
- #134262
- #134264
- #134271
- #134509
- #134521
- #134524
- #134525
- #136124
(Below follows content that predated the accepted Arbitrary Self Types v2 RFC.)
<details>- [ ] figure out the object safety situation
- [ ] figure out the handling of inference variables behind raw pointers
- [ ] decide whether we want safe virtual raw pointer methods
Object Safety
See https://github.com/rust-lang/rust/issues/27941#issuecomment-332157526
Handling of inference variables
Calling a method on
*const _could now pick impls of the formimpl RandomType { fn foo(*const Self) {} }Because method dispatch wants to be "limited", this won't really work, and as with the existing situation on
&_we should be emitting an "the type of this value must be known in this context" error.This feels like fairly standard inference breakage, but we need to check the impact of this before proceeding.
Safe virtual raw pointer methods
e.g. this is UB, so we might want to force the call
<dyn Foo as Foo>::barto be unsafe somehow - e.g. by not allowingdyn Footo be object safe unlessbarwas anunsafe fntrait Foo { fn bar(self: *const Self); } fn main() { // creates a raw pointer with a garbage vtable let foo: *const dyn Foo = unsafe { mem::transmute([0usize, 0x1000usize]) }; // and call it foo.bar(); // this is UB }However, even today you could UB in safe code with
mem::size_of_val(foo)on the above code, so this might not be actually a problem.More information
There's no reason the
selfsyntax has to be restricted to&T,&mut TandBox<T>, we should allow for more types there, e.g.
</details>trait MyStuff { fn do_async_task(self: Rc<Self>); } impl MyStuff for () { fn do_async_task(self: Rc<Self>) { // ... } } Rc::new(()).do_async_stuff();Travis Cross at 2024-08-16 05:33:03
Why would you need this? Why wouldn't you write an impl like this:
impl MyStuff for Rc<()> { fn do_async_task(self) { // ... } }I'd rather define the trait different. Maybe like this:
trait MyStuff: Rc { fn do_async_task(self); }In this case, Rc would be a trait type. If every generic type implemented a specific trait (this could be implemented automatically for generic types) this seems more understandable to me.
Fabio Krapohl at 2017-09-28 01:52:39
This could only be allowed for
traitmethods, right?For inherent methods, I can't
impl Rc<MyType>, but ifimpl MyTypecan add methods withself: Rc<Self>, it seems like that would enable weird method shadowing.Josh Stone at 2017-09-28 22:02:14
@cuviper
This is still pending lang team decisions (I hope there will be at least 1 RFC) but I think it will only be allowed for trait method impls.
Ariel Ben-Yehuda at 2017-10-01 14:50:17
@porky11
You can't implement anything for
Rc<YourType>from a crate that does not own the trait.Ariel Ben-Yehuda at 2017-10-01 14:51:20
So changes needed:
- remove the current error message for trait methods only, but still have a feature gate.
- make sure
fn(self: Rc<Self>)doesn't accidentally become object-safe - make sure method dispatch woks for
Rc<Self>methods - add tests
Ariel Ben-Yehuda at 2017-10-02 13:02:14
I’ll look into this.
Simon Sapin at 2017-10-02 13:04:01
Note that this is only supported to work with trait methods (and trait impl methods), aka
trait Foo { fn foo(self: Rc<Self>); } impl Foo for () { fn foo(self: Rc<Self>) {} }and is NOT supposed to work for inherent impl methods:
struct Foo; impl Foo { fn foo(self: Rc<Self>) {} }Ariel Ben-Yehuda at 2017-10-02 13:16:27
I got caught in some more Stylo work that's gonna take a while, so if someone else wants to work on this in the meantime feel free.
Simon Sapin at 2017-10-06 16:41:28
Is this supposed to allow any type as long as it involves
Self? Or must it implDeref<Target=Self>?trait MyStuff { fn a(self: Option<Self>); fn b(self: Result<Self, Self>); fn c(self: (Self, Self, Self)); fn d(self: Box<Box<Self>>); } impl MyStuff for i32 { ... } Some(1).a(); // ok? Ok(2).b(); // ok? (3, 4, 5).c(); // ok? (box box 6).d(); // ok?kennytm at 2017-10-06 17:07:57
I've started working on this issue. You can see my progress on this branch
Michael Hewson at 2017-10-10 17:26:13
@arielb1 You seem adamant that this should only be allowed for traits and not structs. Aside from method shadowing, are there other concerns?
Michael Hewson at 2017-11-02 13:02:05
@mikeyhew
inherent impl methods are loaded based on the type. You shouldn't be able to add a method to
Rc<YourType>that is usable without anyusestatement.Ariel Ben-Yehuda at 2017-11-02 15:12:53
That's it, if you write something like
trait Foo { fn bar(self: Rc<Self>); }Then it can only be used if the trait
Foois in-scope. Even if you do something likefn baz(self: u32);that only works for modules thatusethe trait.If you write an inherent impl, then it can be called without having the trait in-scope, which means we have to be more careful to not allow these sorts of things.
Ariel Ben-Yehuda at 2017-11-02 15:31:34
@arielb1 Can you give an example of what we want to avoid? I'm afraid I don't really see what the issue is. A method you define to take
&selfwill still be callable onRc<Self>, the same as if you define it to takeself: Rc<Self>. And the latter only affectsRc<MyStruct>, notRc<T>in general.Michael Hewson at 2017-11-03 04:54:28
I've been trying to figure out how we can support dynamic dispatch with arbitrary self types. Basically we need a way to take a
CustomPointer<Trait>, and do two things: (1) extract the vtable, so we can call the method, and (2) turn it into aCustomPointer<T>without knowingT.(1) is pretty straightforward: call
Deref::derefand extract the vtable from that. For (2), we'll effectively need to do the opposite of how unsized coercions are implemented for ADTs. We don't knowT, but we can can coerce toCustomPointer<()>, assumingCustomPointer<()>has the same layout asCustomPointer<T>for allT: Sized. (Is that true?)The tough question is, how do we get the type
CustomPointer<()>? It looks simple in this case, but what ifCustomPointerhad multiple type parameters and we had aCustomPointer<Trait, Trait>? Which type parameter do we switch with()? In the case of unsized coercions, it's easy, because the type to coerce to is given to us. Here, though, we're on our own.@arielb1 @nikomatsakis any thoughts?
Michael Hewson at 2017-11-04 06:54:48
@arielb1
and is NOT supposed to work for inherent impl methods:
Wait, why do you not want it work for inherent impl methods? Because of scoping? I'm confused. =)
Niko Matsakis at 2017-11-04 10:07:00
@mikeyhew
I've been trying to figure out how we can support dynamic dispatch with arbitrary self types.
I do want to support that, but I expected it to be out of scope for this first cut. That is, I expected that if a trait uses anything other than
self,&self,&mut self, orself: Box<Self>it would be considered no longer object safe.Niko Matsakis at 2017-11-04 10:08:01
@nikomatsakis
I do want to support that, but I expected it to be out of scope for this first cut.
I know, but I couldn't help looking into it, it's all very interesting to me :)
Michael Hewson at 2017-11-04 10:15:02
Wait, why do you not want it work for inherent impl methods? Because of scoping? I'm confused. =)
We need some sort of "orphan rule" to at least prevent people from doing things like this:
struct Foo; impl Foo { fn frobnicate<T>(self: Vec<T>, x: Self) { /* ... */ } }Because then every crate in the world can call
my_vec.frobnicate(...);without importing anything, so if 2 crates do this there's a conflict when we link them together.Maybe the best way to solve this would be to require
selfto be a "thin pointer to Self" in some way (we can't useDerefalone because it doesn't allow for raw pointers - butDeref+ deref of raw pointers, or eventually anUnsafeDereftrait that reifies that - would be fine).I think that if we have the deref-back requirement, there's no problem with allowing inherent methods - we just need to change inherent method search a bit to also look at defids of derefs. So that's probably a better idea than restricting to trait methods only.
Note that the
CoerceSizedrestriction for object safety is orthogonal if we want allocators:struct Foo; impl Tr for Foo { fn frobnicate<A: Allocator+?Sized>(self: RcWithAllocator<Self, A>) { /* ... */ } }Where an
RcWithAllocator<Self, A>can be converted to a doubly-fatRcWithAllocator<Tr, Allocator>.Ariel Ben-Yehuda at 2017-11-05 14:30:00
@arielb1
Because then every crate in the world can call my_vec.frobnicate(...); without importing anything, so if 2 crates do this there's a conflict when we link them together.
Are saying is that there would be a "conflicting symbols for architechture x86_64..." linker error?
Maybe the best way to solve this would be to require self to be a "thin pointer to Self" in some way (we can't use Deref alone because it doesn't allow for raw pointers - but Deref + deref of raw pointers, or eventually an UnsafeDeref trait that reifies that - would be fine).
I'm confused, are you still talking about
frobnicatehere, or have you moved on to the vtable stuff?Michael Hewson at 2017-11-05 14:52:11
I'm confused, are you still talking about
frobnicatehere, or have you moved on to the vtable stuff?The deref-back requirement is supposed to be for everything, not only object-safety. It prevents the problem when one person does
struct MyType; impl MyType { fn foo<T>(self: Vec<(MyType, T)>) { /* ... */ } }While another person does
struct OurType; impl OurType { fn foo<T>(self: Vec<(T, OurType)>) {/* ... */ } }And now you have a conflict on
Vec<(MyType, OurType)>. If you include the deref-back requirement, there is no problem with allowing inherent impls.Ariel Ben-Yehuda at 2017-11-05 15:01:48
@arielb1 , you suggest that the point of this is to get around the coherence rules, but I can't see how this wouldn't fall afoul of the same incoherence that the orphan rules are designed to prevent. Can you explain further?
Secondly, the syntax is misleading. Given that
fn foo(&mut self)allows one to writeblah.foo()and have it desugar tofoo(&mut blah), one would intuitively expectfn foo(self: Rc<Self>)to allow one to writeblah.foo()and desugar tofoo(Rc::new(blah))... which, indeed, would obviously be pointless, but the discrepancy rankles....Oh bleh, in my experiments I'm realizing that
fn foo(self: Self),fn foo(self: &Self), andfn foo(self: &mut Self)are all surprisingly allowed alternatives tofn foo(self),fn foo(&self), andfn foo(&mut self)... and that, astonishingly,fn foo(self: Box<Self>)is already in the language and functioning in the inconsistent way that I'm grumpy about in the above paragraph. Of course we special-casedBox... :Pbstrie at 2017-11-16 19:56:51
I'm all for supporting extra types for
Self— I've been looking forward to it for three years.Jake Goulding at 2017-11-17 00:32:37
@shepmaster feel free to try them out! Now that bors has merged my PR, they're available on nightly behind the
arbitrary_self_typesfeature gate: https://play.rust-lang.org/?gist=cb47987d3cb3275934eb974df5f8cba3&version=nightly@bstrie I don't have as good a handle on the coherence rules as @arielb1, but I can't see how arbitrary self types would fall afoul of them. Perhaps you could give an example?
Secondly, the syntax is misleading. Given that fn foo(&mut self) allows one to write blah.foo() and have it desugar to foo(&mut blah), one would intuitively expect fn foo(self: Rc<Self>) to allow one to write blah.foo() and desugar to foo(Rc::new(blah))... which, indeed, would obviously be pointless, but the discrepancy rankles.
For the few methods that use non-standard self types, I get that this could be annoying. But they should be used sparingly, only when needed: the self type needs to Deref to
Selfanyway, so having the method take&selfor&mut selfwould be preferred because it is more general. In particular, takingself: Rc<Self>should only be done if you actually need to take ownership of theRc(to keep the value alive past the end of the method call).Michael Hewson at 2017-11-17 08:14:13
Secondly, the syntax is misleading. Given that fn foo(&mut self) allows one to write blah.foo() and have it desugar to foo(&mut blah), one would intuitively expect fn foo(self: Rc) to allow one to write blah.foo() and desugar to foo(Rc::new(blah))
I don't find that "inversion principle" intuitive. After all, you can't call
fn foo(&mut self)if yourselfisn't mutable. If you have afoo(&mut self), you need to pass it something that is basically an&mut Self. If you have afoo(self: Rc<Self>), you need to pass it something that is basically anRc<Self>.More than that, we already have
self: Box<Self>today with no "autoboxing".Ariel Ben-Yehuda at 2017-11-20 17:46:31
Recent Nightlies emit a lot of warnings like this when compiling Servo:
warning[E0619]: the type of this value must be known in this context --> /Users/simon/projects/servo/components/script/dom/bindings/iterable.rs:81:34 | 81 | dict_return(cx, rval.handle_mut(), true, value.handle()) | ^^^^^^^^^^ | = note: this will be made into a hard error in a future version of the compilerFirst, I had to do git archeology in the rust repo to find that that warning was introduced bc0439b3880808e1385da4b99964d5d506f76e3f, which is part of #46837, which links here. The error message should probably include some URL to let people read further details and context, ask questions, etc.
Second, I have no idea what this warning is telling me. Why is this code problematic now, while it wasn’t before? What should I do to fix it? The error message should explain some more.
Simon Sapin at 2017-12-23 14:36:34
The breakage from this change seems to be larger than it's normally allowed for compatibility warnings. A crater run wasn't performed, but if even rustc alone hits multiple instances of broken code (https://github.com/rust-lang/rust/pull/46914), it means than effect on the ecosystem in general is going to be large.
Vadim Petrochenkov at 2017-12-23 14:51:55
@SimonSapin I'm working on changing that warning to a future-compatibility lint right now (https://github.com/rust-lang/rust/pull/46914), which will reference a tracking issue that explains the reason for the change; I still have to finish writing the issue though so right now it doesn't explain anything
Michael Hewson at 2017-12-23 15:04:45
I guess we didn't realize there would be so much breakage from that PR. Right now, I'm finding lots of places in rust-lang/rust that produce this warning, and they are only popping up now that I'm turning it into a full-blown lint, which is turned into a hard error by
deny(warnings).Michael Hewson at 2017-12-23 15:15:19
Can anyone give a summary of what the status is here? Given that we have two accepted, high-priority RFCs mentioning this feature (https://github.com/rust-lang/rfcs/pull/2349 and https://github.com/rust-lang-nursery/futures-rfcs/pull/2), it's concerning that we still don't have even anything resembling an RFC for this. Since I assume (?) that the RFCs in question are going to end up influencing things in libstd I suppose it's not absolutely imperative that we stabilize this anytime soon, but even having an idea of what the design goals of this feature are would be a good start to making sure we don't box ourselves into a corner somehow. It seems unprecedented for a feature to progress so far without even a pre-RFC...
bstrie at 2018-05-12 03:28:16
@bstrie there is an RFC PR that @withoutboats has opened. As far as implementation is concerned, everything in that RFC is implemented behind the
arbitrary_self_typesfeature gate, except that all custom receiver types are considered non-object-safe, and dynamic dispatch hasn't been implemented. I've been working on implementing that, and you can follow this WIP PR for updates.There are also some things that are implemented that aren't included in that RFC: notably, raw pointer method receivers, and receivers that deref transitively to
Selfrather than directly implementingDeref<Target=Self>, e.g.&Rc<Self>.I hope that clears things up a bit. It doesn't completely address your concerns about RFCs being merged that are depending on an unimplemented, undocumented, and perhaps incompletely designed language feature, but hopefully, once I'm finished implementing the object-safety checks and dynamic dispatch, and that RFC gets merged, the foundation for those other RFCs will be less rocky.
Michael Hewson at 2018-05-12 09:18:10
@mikeyhew thanks! Up top I've added a link to the RFC so that people can follow along easier. To be clear, do we intend not to support object safety in receivers for this initial implementation pass, or is that just yet-to-be-implemented?
bstrie at 2018-05-12 18:16:52
@bstrie the initial implementation pass is finished, and it did not include object-safety. The next pass, which I'm working on right now, will include object-safety.
Michael Hewson at 2018-05-12 20:20:48
There are interesting covarint uses of this :
fn foo<'a,E,F: FnOnce() -> Result<&'a mut Cat,E>>(self: F, bar: Bar) -> Result<Dog,E> { let h = heavy_setup(bar); let r = self()?.light_manipulations(h); Ok( heavy_conclusions(r) ) }Now
foo(|| &mut my_mutex.bla.lock()?.deref_mut().blabla)locks only briefly in the middle, but the API containingfooneed not plan around any particular struct hierarchy or locking scheme, whiole still enforcing thatheavy_setupbe run beforeheavy_conclusions.Jeff Burdges at 2018-07-05 17:07:03
@burdges that wouldn't be possible, because the
selfargument must Deref toSelf. Why does the closure need to be a method receiver and not just a normal argument?You could create a
Thunk<T>that holds a closure that gets called when it's dereferenced. As far as I know, though, Deref impls aren't supposed to panic, so that might be an antipattern.Michael Hewson at 2018-07-06 02:29:39
Yes, a normal argument works just fine of course, and works on stable. I even used it that way by mistake, instead of writing
(|| &mut my_mutex.bla.lock()?.deref_mut().blabla).foo().At first blush, this seemingly made sense as a method receiver, but yeah arbitrary self types do sounds far off now, and normal arguments might make more sense and/or improve type inference and error messages.
Jeff Burdges at 2018-07-07 12:03:08
Is this planned to be in 2018 edition?
eliantor at 2018-07-09 15:21:43
@eliantor no it isn't. It still isn't fully implemented, and once it is, it will probably still be a while until it is stabilized. The RFC isn't merged yet either, and I hope to get a working implementation before we do merge it.
Michael Hewson at 2018-07-17 23:09:16
I'm having some trouble with method resolution when passing through multiple dereferences:
#![feature(pin, arbitrary_self_types)] use std::marker::Unpin; use std::ops::{Deref, DerefMut}; #[derive(Copy, Clone)] pub struct Pin<P> { pointer: P, } impl<P, T: ?Sized> Deref for Pin<P> where P: Deref<Target = T>, { type Target = T; fn deref(&self) -> &T { &*self.pointer } } impl<P, T: ?Sized> DerefMut for Pin<P> where P: DerefMut<Target = T>, T: Unpin, { fn deref_mut(&mut self) -> &mut T { &mut *self.pointer } } trait Future { type Output; fn poll(self: Pin<&mut Self>) -> Self::Output; } impl Future for () { type Output = (); fn poll(self: Pin<&mut Self>) -> Self::Output { } } fn test(pin: Pin<&mut ()>) { pin.poll() }https://play.rust-lang.org/?gist=77af55591988d9cf2aa216e8b4fcb8cb&version=nightly&mode=debug&edition=2015
@mikeyhew does this look like a bug or am I doing something wrong?
srrrse at 2018-08-29 22:15:02
Am I right, there are no way to declare vector of Futures yet?:
Vec<Box<Future<Output = u32>>>Andrey Tkachenko at 2018-08-30 08:36:44
@withoutboats that looks like a bug.
pin.poll()should work.Future::poll(pin)does work, so it is indeed a problem with method lookup.@andreytkachenko as far as I know, the
Futuretrait from libcore is not object safe yet. So no, that's not possible yet. OncePinis considered object safe by the compiler, though, it will be.Michael Hewson at 2018-08-30 23:17:07
@mikeyhew the bug seems connected to #53843 which manifests on stable and isn't tied to this feature
@andreytkachenko to work around the problem, there's currently a type called
FutureObjyou can usesrrrse at 2018-08-31 00:47:45
@mikeyhew @withoutboats Thank you for your replies, I'll take a look on
FutureObjAndrey Tkachenko at 2018-08-31 06:46:57
Now that
FutureObjis removed in nightly, is there another workaround available?Jasper at 2018-10-13 04:46:35
I didn't know
FutureObjwas removed already, but it should be obsolete in a matter of weeksMichael Hewson at 2018-10-13 08:21:07
Now that FutureObj is removed in nightly, is there another workaround available?
FutureObjis now being provided by thefuturescrate, because other changes to the API made it unnecessary for it to be in std.srrrse at 2018-10-13 13:26:42
@mikeyhew need futures-preview-0.7
Alex at 2018-10-14 01:58:53
#54383 was just merged (🎉), which means that receiver types like
Rc<Self>andPin<&mut Self>are now object-safe.The next step is to make the receiver types in the standard library be usable as method receivers without the arbitrary_self_types feature, while still requiring the feature flag for ones we don't want to stabilize. Since receiver types are checked using the
Dereftrait, andDerefis stable, we need some way to differentiate between receiver types that are "blessed" and those that require the feature flag. I have thought of a few ways to do this:- use an unstable
#[receiver]attribute on the struct definition. The problem with this is there is that we would only check the outermost struct, and in the case ofPin<P>you could makePbe some random type that derefs toSelfto circumvent the checks. - use an unstable
Receiver<T>trait, where we check if the receiver type implementsReceiver<Self>.TimplementsReceiver<T>,Rc<T>implementsReceiver<U>ifT: Receiver<U>, etc. This would be cool, but unfortunately it doesn't work with the current trait system due to overlapping impls. - use an unstable
Receivertrait that is a subtrait ofDeref, and check for it during the autoderef loop when we are checking if the receiver transitively derefs toSelf. This is the most promising option.
I'm working on a branch that does option 3 right now, and will hopefully have a PR up soon.
Michael Hewson at 2018-11-03 06:45:21
- use an unstable
@mikeyhew What about using the object safety traits as the limiter, since they are also unstable?
srrrse at 2018-11-04 19:34:08
@withoutboats That could work for trait methods, but I'm not sure how that would work with inherent methods, since
Selfisn't a type parameterMichael Hewson at 2018-11-05 05:43:33
@mikeyhew thanks, that makes sense. I don't think its important how its implemented, but ideally nothing new would show up in the public API, since this just a hack to let us use std defined pointers on stable. Is it possible to make the
Receivertrait private and only check that its implemented if thearbitrary_self_typesfeature flag is not enabled (i.e. on stable)?srrrse at 2018-11-08 13:20:14
Technically it couldn't be private, since it's used in liballoc. But I could use
#doc(hidden)if you wantMichael Hewson at 2018-11-08 18:29:14
Today I encountered a problem where
f(self: Rc<Self>)orf(self: &Rc<Self>)for objects of typeRc<dyn Trait>would be the right solution. All workarounds are somewhat ugly. Aggravating the situation, the methods are part of a public interface, affecting the offshoots of my code base.For this reason, I am positively surprised at the current progress.
Finn at 2018-11-16 18:24:55
Update: now that https://github.com/rust-lang/rust/pull/56805 has merged,
self: Rc<Self>andself: Arc<Self>are usable without a feature flag on nightly. Also due to #56805, any receiver type that works on its own will also work if wrapped inPin<..>. This currently requires thepinfeature, but that requirement will be removed once https://github.com/rust-lang/rust/pull/56939 is merged.Michael Hewson at 2018-12-24 07:51:18
I'm trying to make a GUI library, and I see the need for a
self: Rc<RefCell<Self>>(because I need to mutate existing UI elements). (Strictly speaking, it should beGc<...>, but we're not there yet).UI elements need to be able to store (
Weak?) references to correlated objects.Kalle Samuels at 2018-12-27 04:59:29
@njaard that unfortunately doesn't work, even with
#!feature(arbitrary_self_types), becauseRefCelldoesn't implementDeref. Conceptually, there's nothing wrong with it as a receiver type, but we would have to change theDereftrait hierarchy so thatDerefandDerefMuthad a common supertrait, instead ofDerefMutrequiringDeref. That would be tough to do at this point, since any change to those traits would have to be backward-compatible.Michael Hewson at 2018-12-27 05:45:59
I have a concern about the design of this feature. Consider:
struct Foo(u32); impl Foo { fn foo(Self(x): Self) { println!("OK!"); } // <-- [A] } fn main() { let x = Foo(5); x.foo(); }I have written lots of code where I would love to (soon) be able to write
selfpatterns that destructureselfas in line[A]above, just like we can destruct any other parameter with similar syntax today. It seems like the current design of this feature requires theselfpattern to be exactlyself. I worry this would be further fossilizing the mistake (IMO) made in https://github.com/rust-lang/rust/issues/16293#issuecomment-185906859.Brian Smith at 2018-12-27 07:38:34
Specifying the type for self looks very strange - I have always believed that the self is connected with the impl Self. Why instead of self: Wrap<Self> not write impl Wrapper<Self>:
trait Future { type Output; fn poll(self: Pin<&mut Self>) -> Self::Output; } impl Future for () { type Output = (); fn poll(self: Pin<&mut Self>) -> Self::Output { } }->
trait Future { type Output; fn poll(self) -> Self::Output; } impl Future for Pin<&mut ()> { type Output = (); fn poll(self) -> Self::Output { } }Andrey Chesnokov at 2018-12-27 09:30:54
that the self is connected with the impl Self
It is related. All of these are valid in stable Rust:
struct Foo; impl Foo { fn a0(self) {} fn a1(self: Self) {} fn b0(&self) {} fn b1(self: &Self) {} fn c0(&mut self) {} fn c1(self: &mut Self) {} fn d0(self: Box<Self>) {} }There happen to be shorthand versions for the three most common versions. This feature just expands the other types allowed besides
Box.Jake Goulding at 2019-01-02 21:22:37
There happen to be shorthand versions for the three most common versions. This feature just expands the other types allowed besides
Box.OK, I withdraw my objection then. I had no idea we could already do
self: &Selfalready.Brian Smith at 2019-01-02 21:32:25
@briansmith I think @shepmaster was replying to @chessnokov, not to you. Regarding what you said:
I would love to (soon) be able to write self patterns that destructure self as in line [A] above
The thing is, Rust needs the
selfkeyword to tell the difference between an ordinary associated function and a method. So even if destructuring function arguments was allowed, it probably wouldn't be allowed for methods. But you can always destructure the argument at the top of your function body, e.g. withlet Foo(x) = self;Michael Hewson at 2019-01-02 21:45:33
even if destructuring function arguments was allowed
It is.
fn add_tuple((a, b): (u32, u32)) -> u32 { a + b }Simon Sapin at 2019-01-02 21:59:40
Just played around with this a bit and found that whilst a receiver of
self: *mut Selfis allowed,self: NonNull<Self>isn't (in a trait) and produces a compile error.error[E0307]: invalid method receiver type: std::ptr::NonNull<Self> --> src/main.rs:38:29 | 38 | unsafe fn initialize(self: NonNull<Self>, arguments: Self::InitializeArguments); | ^^^^^^^^^^^^^ | = note: type of `self` must be `Self` or a type that dereferences to it = help: consider changing to `self`, `&self`, `&mut self`, or `self: Box<Self>`To my mind, where I know the receiver is a non-null pointer, but not necessarily initialized properly, this is a useful way of writing code as documentation; particularly for a trait, where the implementor can be certain he hasn't been given null.
It might also be worth considering
Option<NonNull<Self>>, too, even if only to warn that it would be better expressed as*mut Self.Raphael Cohn at 2019-01-23 13:04:23
self: NonNull<Self>isn'tBecause
NonNulldoesn't implementDeref. See also #48277.considering
Option<NonNull<Self>>Optionalso doesn't implementDeref. AFAICT it cannot, at least not without panicking when it'sNone, which seems like a very bad ergonomic idea.Jake Goulding at 2019-01-23 14:41:58
*mut Selfdoesn’t implementDerefeither.Simon Sapin at 2019-01-23 15:21:43
*mut Selfdoesn’t implementDerefeither.Sure, but that was a specific addition (See also #46664):
Maybe the best way to solve this would be to require
selfto be a "thin pointer to Self" in some way (we can't useDerefalone because it doesn't allow for raw pointers - butDeref+ deref of raw pointers, or eventually anUnsafeDereftrait that reifies that - would be fine).Raw pointers are definitely special-cased in the compiler, while
NonNullis a regular struct and not a language item.Jake Goulding at 2019-01-23 15:57:13
Raw pointers are definitely special-cased in the compiler
Indeed.
NonNull<Self>is something that really should be supported at some point (at least if raw pointer receivers are), but it would require a separate trait fromDerefthat returns*const Self::Target, and we would need an RFC for thatMichael Hewson at 2019-01-23 20:05:33
@mikeyhew Another RFC? That's disappointing to learn. I haven't got the desire to go down that route. At least the thought's there.
@shepmaster I the code I am now commonly writing when dealing with low-level pointer stuff,
Option<NonNull<T>>comes up quite a bit. It'd just be nice to have the compiler either deal with it by assuming*mut Self.Raphael Cohn at 2019-01-24 09:21:14
This is the trait that I was thinking of. Both
NonNull<T>andOption<NonNull<T>>could implement it.trait DerefRaw { type Target; fn deref_raw(&self) -> *const Self::Target; }Michael Hewson at 2019-01-25 06:04:50
I’m really not sure about doing this for
Option<NonNull<T>>. Isn’t the whole point of it that you must check for None/null before accessing the pointer?Simon Sapin at 2019-01-25 08:15:37
Option<NonNull<T>>is, in many cases, much easier to work with than*mut Tand contains far more expressive power. It also more 'naturally' inevitably gets created by code working withNonNull<T>; suddenly moving to*mut Tis weird, and, less-Rusty; one can't simply doSome(non_null)as a result of a function, say. It also allows 'balance' inmatchclauses, which are shorter to read and more ergonomic thanifwithis_null(); it does not require the casual reader of code to suddenly understand as much of raw pointers - knowledge of Rust'sOptiontype is enough; and lastly, it makes up for the absence ofis_not_null(), for which!x.something.is_null()reads appalling and requires more visual processing (and is easier to miss).Raphael Cohn at 2019-01-25 13:34:59
Sorry, I should have clarified. Using
Option<NonNull<T>>in general sounds good. What I’m not sure about is implementingDerefRawas proposed above forOption<NonNull<T>>. Sure, it has an obvious implementation, but converting to a nullable*const Tseems to defeat the point of usingOption<NonNull<T>>in the first place.Simon Sapin at 2019-01-25 13:43:46
the big question around raw pointers is the requirements as to the validity of vtables for invalid pointers. I’m not sure what we should do, but if we made Option<NonNull<T>> impl DerefRaw we’d be assuming the vtable could be invalid; calling a dynamic method on None would just always be UB, I believe.
On Friday, January 25, 2019, Simon Sapin notifications@github.com wrote:
Sorry, I should have clarified. Using Option<NonNull<T>> in general sounds good. What I’m not sure about is implementing DerefRaw as proposed above for Option<NonNull<T>>. Sure, it has an obvious implementation, but converting to a nullable *const T seems to defeat the point of using Option<NonNull<T>> in the first place.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/44874#issuecomment-457576646, or mute the thread https://github.com/notifications/unsubscribe-auth/AIpL0Jj1TskgEDDbOJ_vjVIk9U258spjks5vGwougaJpZM4Pk3fK .
srrrse at 2019-01-25 14:41:55
Unless we have decided a representation of a dynamic-sized
enumI'm opposed toOption<NonNull<dyn Stuff>>.kennytm at 2019-01-25 16:37:17
@withoutboats Ohh, good point, I forgot about that. That means there's potentially a real difference between
*mut TandOption<NonNull<T>>.@kennytm I think you're confusing
Option<NonNull<dyn Trait>>withOption<dyn Trait>Michael Hewson at 2019-01-25 19:22:56
@mikeyhew Oh right, sorry.
kennytm at 2019-01-26 06:58:58
@mikeyhew
Are you talking about
Option<NonNull<*mut dyn Trait>>(which is a type that makes sense) or aboutNonNull<dyn Trait>(which I don't think makes much sense)?Ariel Ben-Yehuda at 2019-01-26 19:05:27
BTW, from an API design standpoint, one thing that would work would be to have a special
ArbitrarySelfTypePtrtrait (bikeshed on name):trait ArbitrarySelfTypePtr { type Next: ?Sized; } impl<T: Deref + ?Sized> ArbitrarySelfTypePtr for T { type Next = T::Target; } impl<T: ?Sized> ArbitrarySelfTypePtr for *mut T { type Next = T; } impl<T: ?Sized> ArbitrarySelfTypePtr for *const T { type Next = T; } impl<T: ?Sized> ArbitrarySelfTypePtr for NonNull<T> { type Next = T; } // Maybe, if we really want that? impl<T: ?Sized> ArbitrarySelfTypePtr for Option<T> { type Next = T; }Ariel Ben-Yehuda at 2019-01-26 19:08:14
The "ArbitrarySelfTypePtr" exists solely so we can have a "sane" deref chain for inherent methods etc.
Ariel Ben-Yehuda at 2019-01-26 19:09:45
@arielb1 I like your suggested
ArbitrarySelfTypePtrtrait because it logically documents what is and isn't possible for aselftype. It would make it easy to refer to in compiler errors, egan arbitrary self type must implement the trait ArbitrarySelfTypePtr.BTW, for the avoidance of doubt, I'm not absolutely wedded to the
Option<NonNull<T>>requirement, it's just that it logically seemed it should exist.Raphael Cohn at 2019-01-27 09:33:02
Did anyone consider
f(self: Weak<Self>)? In my point of view this seems reasonable sinceArcandRcare already allowed.Michael Watzko at 2019-04-01 14:26:39
That seems unsafe since the self pointer could then be dropped while yhe method is executing, leaving
selfto dangle.mark at 2019-04-01 14:50:38
That seems unsafe since the self pointer could then be dropped while yhe method is executing, leaving
selfto dangle.Hmm? Am I missing something? (probably) Isn't it just an ergonomic 'shortcut', being able to write
f(self: Weak<Self>)instead of i.e.f(me: Weak<Self>)? Thus it is still required to callself.upgrade()to accessSelfwhich isn't unsafe.Michael Watzko at 2019-04-01 15:02:50
Weak<Self>is in the same boat as*const/mut SelfandNonNull<Self>: it's safe to call a method with it, but it doesn't implementDeref. Right now the unstablearbitrary_self_typesfeature only supports receiver types that implementDeref, plus*const/mut Selfas a special case.Michael Hewson at 2019-04-01 15:56:50
Weak<Self>is in the same boat as*const/mut SelfandNonNull<Self>: it's safe to call a method with it, but it doesn't implementDeref. Right now the unstablearbitrary_self_typesfeature only supports receiver types that implementDeref, plus*const/mut Selfas a special case.Thank you for the clarification!
Any chance
Weak<Self>could be added to the additional special cases? (caution rust-compiler-newbie speaking) Looking at the current state, it seems that one would need to handle another speicialised case in receiver_is_valid or more precisely in Autoderef and it would need to be called like include_raw_pointers. From the naming scheme that feels odd(?) to me, becauseWeak<Self>can fail to "Deref" toSelf(well, as you mentioned, like*const/mut Self).Maybe another reason for an
ArbitrarySelfType-likish trait?Michael Watzko at 2019-04-01 17:10:48
Yeah, I think using a separate trait from
Derefas @arielb1 suggested would be a good idea. There is a running list of types that would benefit from being method receivers, but can't implementDeref:*const/mut SelfWeak<Self>(bothRcandArcversions)NonNull<Self>RefCell<Self>, and by composition,Rc<RefCell<Self>>- similarly,
Mutex<Self> Option<Self>andResult<E, Self>(these ones are questionable)
Of the above list, all but
OptionandResultcould produce a valid*const Selfand therefore could implement a hypotheticalUnsafeDereftrait. However, I'm starting to think that it would be better to have a trait that is specifically used for method lookup, whether or not it is decided thatOption<Self>should be usable as a method receiver or not.Also, here is a potential name for the trait:
MethodReceiverMichael Hewson at 2019-04-02 00:19:16
We can do a similar trick to
CoerceUnsizedand whitelistMethodReceiverin the compiler for pointers/references, and require that any structs that implementMethodReceiver, themselves contain exactly one field that reaches the target type after any number of applications ofMethodReceiver.This differs from
CoerceUnsizedin that it uses an associated type, likeDeref, instead of a parameter, so it's less flexible, but it allows repeated application in the compiler.Doing it without a method for performing a "deref" operation means
x.foo()-><_>::foo(x)sugar can be decoupled from any kind of safety concerns. (That still leavesx.foo()->(***x).foo()sugar but maybe those can be decoupled?)Eduard-Mihai Burtescu at 2019-04-09 18:15:17
Wanted to comment a small pain point I've encountered using this feature I haven't seen mentioned yet.
If we have a method receiving a
*const T, such asfn len(self: *const Self), we can't call it directly with a mutable pointer,*mut T, although we can just cast it and call it manually with(var as *const T).len(). Might be worth adding a special case for this, just as we can call&selfmethods with a&mutreference.Example:
#![feature(arbitrary_self_types)] pub trait Test { fn a(self: *const Self); } impl Test for u32 { fn a(self: *const Self) {} } pub fn main() { let mut a = 5; let a_ptr: *const u32 = &mut a; // Works fine a_ptr.a(); let a_mut_ptr: *mut u32 = &mut a; // Currently works (a_mut_ptr as *const u32).a(); // Currently results in error "method not found in `*mut u32`" // Would result to calling `(a_mut_ptr as *const u32).len()` a_mut_ptr.a(); }Filipe Rodrigues at 2020-09-26 00:49:54
I guess one limitation of using an associated type for
MethodReceiverwould be that we couldn't provide a blanketimpl<T> MethodReceiver<T> for T, which unless we wanted to try performing some kind of auto-ref on arbitrary-self-accepting methods would prevent something like this from working (based on the currentDeref-based design): Playground(In my use-case there though I don't have a problem requiring
.as_mut().poison(), because I don't expect it to be as common as.lock().poison()), but I think we should also keep the generic use-case for this trait in mind besides just concrete arbitrary-self-types.Ashley Mannix at 2021-02-05 05:56:29
Also, is the expectation that this
MethodReceivertrait will be publicly implementable?If so, it seems like we'd probably end up recommending types that implementing
Deref(where there's already an understanding that you don't add inherent methods to avoid shadowing) also implement this trait. The bounds in my example would then becomeMethodReceiver<Target = Self> + DerefMut<Target = Self>, which seems ok.If not, I think we shouldn't implement it for any non-fundamental containers, like
Mutex, otherwise we'd be making thestdimplementations of those types special compared to ecosystem ones.Ashley Mannix at 2021-02-06 01:40:15
Currently lifetimes cannot be elided when using
arbitrary_self_types:struct A(u32); impl A { // compile error pub fn value(self: &Arc<Self>) -> &u32 { &self.0 } // ^ error: missing lifetime specifier. this function's return type contains a borrowed value, but there is no value for it to be borrowed from // ok pub fn value_lifetimed<'a>(self: &'a Arc<Self>) -> &'a u32 { &self.0 } }And The Rust Reference says: https://doc.rust-lang.org/reference/lifetime-elision.html
If the receiver has type &Self or &mut Self, then the lifetime of that reference to Self is assigned to all elided output lifetime parameters.
Should this rule also be expanded to arbitary self types?
f32y at 2021-07-24 07:38:59
Is that a feature like UFCS(Uniform Function Call Syntax)?
Danube at 2021-08-12 16:51:26
Has there been any discussion about
self: &Cell<T>? That seems especially interesting in that&Cell<T>is (as of Rust 1.37) convertible from&mut T, so methods taking&Cell<T>could be useful in existing programs, without any change in data layout. I understand there's aDerefrequirement that currently stands in the way, but from the thread above it sounds like people are considering relaxing that requirement somehow.Jack O'Connor at 2021-08-14 20:48:57
What could be the reason I cannot get a receiver with an extra trait bound (e.g.
Send) to work? If you remove the+ Sendbound on all impls, everything works fine.struct Ptr<T: ?Sized + Send>(Box<T>); // impls for Deref, CoerceUnsized and DispatchFromDyn trait Trait: Send { fn ptr_wrapper(self: Ptr<Self>) -> i32; } impl Trait for i32 { fn ptr_wrapper(self: Ptr<Self>) -> i32 { *self }} fn main() { let p = Ptr(Box::new(5)) as Ptr<dyn Trait>; assert_eq!(p.ptr_wrapper(), 5); }error[E0038]: the trait "Trait" cannot be made into an objectdimpolo at 2021-12-13 02:42:07
Going to retag this as wg-traits; kind of the best wg- label right now, even though we don't really discuss this
Jack Huey at 2022-01-29 01:01:59
Input from T-lang Backlog Bonanza meeting from 2022-02-09: Can anyone write down a summary of the status of this feature?
Should any of the checkboxes in the description be checked off now as done? Do new checkboxes need to be added?
Felix S Klock II at 2022-03-14 18:23:55
Changing WG-traits to T-types. We discussed this in today's types triage meeting and ultimately decided that there are types decisions to be made here, so the team label feels appropriate. We don't have any concrete plans for this right now though.
Jack Huey at 2022-06-17 13:32:23
Here's a summary of this issue as requested in this comment @pnkfelix.
Disclaimer: I haven't been involved so this may not be exactly right. But I'm interested in taking it forward so this summary was useful to compile anyway.
What works now
- Nightly only, behind feature flag
arbitrary_self_types:- Any type implementing
Deref<Target=T>may accept method calls on behalf ofT. (@mikeyhew's PR https://github.com/rust-lang/rust/pull/45870) *const Tand*mut Tmay accept method calls (@mikeyhew's PR https://github.com/rust-lang/rust/pull/46664)- Dynamic dispatch and object safety were implemented (@mikeyhew's PR https://github.com/rust-lang/rust/pull/54383) using
DispatchFromDyn
- Any type implementing
- Stabilized already:
Rc<Self>,Arc<Self>andPin<P>may be used as a receiver type (@withoutboats' issue https://github.com/rust-lang/rust/issues/55786 amd @mikeyhew's PR https://github.com/rust-lang/rust/pull/56805)
Other work done
- @withoutboats raised an RFC (https://github.com/rust-lang/rfcs/pull/2362) which was marked as postponed in August 2019 (https://github.com/rust-lang/rfcs/pull/2362#issuecomment-526372416)
Concerns
This attempts to be a complete summary of all known concerns raised in the discussions (at least, those visible on github). Please shout if I've missed something!
-
Major things we can defer till later (i.e. not stabilize yet):
- Object safety. The algorithm for object safety is quite complex and relies on some object layout assumptions described in
DispatchFromDyn. There are not currently any known problems here but it seems wise to be cautious in stabilizing this bit. For example, "before this gets merged, I think we should either remove the part about object safety, or say that the way object-safety is determined will remain unstable for a while, and will probably need a new RFC for a final decision to be made". - It only works on types implementing
Deref. There have been many requests to call methods on types which can't implementDeref: for instance,RefCell,NonNull,Weak. A summary is here. It's proposed this be solved using aMethodReceivertrait (formerly known asArbitrarySelfTypePtr). This also helps with some of the C++ interop use cases which is why I'm interested..
- Object safety. The algorithm for object safety is quite complex and relies on some object layout assumptions described in
-
Smaller issues that we probably need to address in the next bit of stabilization:
- Calling methods on raw trait pointers with invalid vtables might be UB. Any use of a raw pointer in Rust typically requires
unsafe. Calling trait methods (*const dyn Trait, for instance) via arbitrary self types might "use" the vtable, and therefore should be unsafe. (This might also apply to future extension via use ofMethodReceiver.) - Handling of inference variables. As noted in the initial issue description
*const _may require some specific error messages just like&_does.
- Calling methods on raw trait pointers with invalid vtables might be UB. Any use of a raw pointer in Rust typically requires
-
Smaller issues which we can defer till later:
- Lifetime elision isn't yet done. Minor inconvenience? Example, example.
- Can't call
*const Tmethods on a*mut T. Minor inconvenience.
-
Issues which are resolved or aren't relevant to stabilization:
- Method shadowing. In the early days of this effort there were concerns about method shadowing and a proposal of an additional orphan rule. It was agreed that there are no problems here if we rely on
Deref. - Name of the feature. Some say it should be "custom method receivers" rather than "arbitrary self types".
- Method shadowing. In the early days of this effort there were concerns about method shadowing and a proposal of an additional orphan rule. It was agreed that there are no problems here if we rely on
Next steps
This seems to decompose nicely into several sequential RFCs.
- (already done in https://github.com/rust-lang/rust/pull/56805!) Stabilize support for
Rc<Self>,Arc<Self>andPin<P>using a hard-codedReceivertrait withincore. (This initial split was proposed here: https://github.com/rust-lang/rust/issues/44874#issuecomment-435565198) - Extend stable support to truly custom method receivers - any type which implements
Deref<Target=T>- but do not yet attempt object safety beyond the hard-coded types which already implement the hiddenReceivertrait. (This split was proposed back here https://github.com/rust-lang/rust/issues/44874#issuecomment-341885532). We should also ensure that this stabilization does not yet allow calls to*const dyn Tor*mut dyn T, or that such calls requireunsafe. We should also ensure that error messages relating to inference variables are great. As far as I can see, there are no other unresolved issues or complexities here. - Stabilize object safety. This would stabilize
DispatchFromDynor similar. - Extend to types which don't or can't support
Derefby implementing aMethodReceivertrait - Minor future extensions in any order: (a) Allow calling
*const Tmethods on a*mut T, (b) Solve lifetime elision, (c) Allow calls to*const dyn Tand*mut dyn T.
The only blocker to doing step 2 is if we decide that we might not want to rely on
Dereffor this feature. But that seems pretty certain by this point...?Adrian Taylor at 2022-11-07 20:21:58
- Nightly only, behind feature flag
Calling methods on raw trait pointers with invalid vtables might be UB. Any use of a raw pointer in Rust typically requires unsafe. https://github.com/rust-lang/rust/issues/44874#issue-260766278. (This might https://github.com/rust-lang/rust/issues/44874#issuecomment-457593696.)
This is closely related to https://github.com/rust-lang/rfcs/pull/3324. There it was decided that upcasting on raw pointers is safe. This means, at the very least, that the safety invariant for raw dyn pointers says that the vtable is correct. Therefore it would only make sense to also make method calls safe.
This means unsafe code constructing invalid raw dyn pointers is currently on shaky grounds -- we likely want to permit some invalid values for intermediate local use, but it is not yet determined which ones.
Ralf Jung at 2022-11-09 08:27:57
It only works on types implementing
DerefOne thing that I want to make sure we know that we're deciding now, is that this will work "by default" with
Deref(whereas we could consider wanting to make it opt-in).This effectively locks us in the future to the
MethodReceiverhaving a blanketimpl<T: Deref> MethodReceiver for T.Mads Marquart at 2022-11-09 15:01:30
@madsmtm said:
One thing that I want to make sure we know that we're deciding now, is that this will work "by default" with
Deref(whereas we could consider wanting to make it opt-in). This effectively locks us in the future to theMethodReceiverhaving a blanketimpl<T: Deref> MethodReceiver for T.I think we can decouple stabilization of
Dereffrom the implementation of aMethodReceivertrait. I think they're orthogonal traits.Here's my understanding - please correct me where I'm wrong!
To figure out the method receiver, rustc currently does 0 or more derefs. These may be built-in
*orDerefoperations.If we want to be able to extract raw pointers to call methods, I think we want 0 or more derefs, followed by one or zero (calls to
MethodReceiver::receiver+ one built-in deref). So I think the existingDerefmachinery is orthogonal to the proposedMethodReceivertrait, and we'll ultimately need both.For instance:
&Box<CppSmartPointer<CppObject>>whereCppSmartPointer: MethodReceiver<Target=CppObject>>would give:
- Built-in deref, to give
Box<CppSmartPointer<CppObject>> - Overloaded
Deref::derefto give&CppSmartPointer<CppObject> - Overloaded
MethodReceiver::receiverto give*const CppObject - Built-in deref to give
CppObject
Those last two steps would always be pretty much the same if we came across a
MethodReceiver. But the chain ofDerefs in steps 1 and 2 can be arbitrary.Another example:
&Rc<RefCell<T>>- Built-in deref to give
Rc<RefCell<T>> - Overloaded
Deref::derefto giveRefCell<T> - Overloaded
MethodReceiver::receiverto give*const T - Built-in deref to give
T
Here's a design sketch of how I think we'd need to implement the
MethodReceivertrait:- New trait:
pub unsafe trait MethodReceiver { // unsafe because rust will dereference the pointer you supply type Target: ?Sized; #[must_use] fn receiver(&self) -> *const Self::Target; }- We put most of the logic for handling this trait within
Autoderef, since then it is reused for checking and candidate resolution stages. Autoderefis used for multiple things, not just method resolution. We'd obviously only want theMethodReceiverresolution to happen in one particular mode, which we can turn on analogously to the currentinclude_raw_pointersmode.- If
Autoderefis in this mode, at each step of the chain it checks whether the target implementsMethodReceiver. If so, it stops iterating, but adds two new steps - one for theMethodReceivercall and one for a built-in deref. Autoderefknows how to emit a new type ofAdjustfor thisMethodReceivercall:Adjust::MethodReceiverCallor similar.rustc_middleknows how to call theMethodReceiver::receivercall when it encounters such an adjustment, just as it currently knows how to callDeref::deref.- Changes to
traits::query::CandidateStepandcheck::method::probe::Pickto represent the possibility of such method calls. (I believe these are driven fromAutoderefso they should benefit from the same logic, but we'll need to store an extra Boolean to denote whether the chain ofDerefsends up in theMethodReceiverextra step).
I think we could do all of that either before or after we stabilize the use of
Dereffor arbitrary self types.In short,
Derefcontinues to be used to iterate through chains of smart pointers or containers which result in more references;- The new
MethodReceivertrait would be sometimes used after that chain to convert to a raw pointer on which a method could be called.
In any case, I'd be happy to work on
MethodReceiverfirst if that's folks' preference.Adrian Taylor at 2023-03-24 23:58:29
- Built-in deref, to give
I am trying to see how far this feature has been worked on and I have a few questions and asks:
- Regarding the "truly custom types" via
Deref. Does this example[1] explain the intention? (playground link to the example, also reproduced below) - Does this also need to work for inherent
impls as well, of types or just the traitimpls of types? I am asking this because this old comment on this very ticket says so. Given that inherentimpls support custom receivers implementing theReceivertrait already[2] - Lastly, this issue does not have any RFC. Would folks here be ok if I wrote (with help!) a pre-RFC? I think we might be at a point we could do this, at least starting summarising this entire effort thus far?
References
[1]
#![no_std] #![feature(arbitrary_self_types)] use core::ops::Deref; trait TrulyCustomTraitExample<T> where T: Deref<Target = Self>, { fn by_deref(self: T) -> Self; } #[derive(Clone)] struct TrulyCustomTypeExample { value: i8, } impl Deref for TrulyCustomTypeExample { type Target = i8; fn deref(&self) -> &Self::Target { &self.value } } impl TrulyCustomTraitExample<TrulyCustomTypeExample> for i8 { fn by_deref(self: TrulyCustomTypeExample) -> Self { *self } } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let t = TrulyCustomTypeExample { value: 1 }; let x = t.clone().by_deref(); assert_eq!(t.value, x); } }[2]
struct S; impl S { fn f(self: std::pin::Pin<&S>) {} }Amanjeev Sethi at 2023-04-25 18:34:36
- Regarding the "truly custom types" via
Hi @amanjeev , I am inching ever-closer to writing an RFC, though I haven't got there yet. I had a chat with @Manishearth and he reckons a bit of pre-discussion by means of blog posts or similar might help recipients understand the context better. If you decide to start the process, that would be great. I'll let you know if I get there first.
I'm not sure your example summarizes the need perfectly. Think of it more like "how can people outside of
stdimplement a smart pointer likeRc"? Currently, they can't: if you havefoo: Rc<Foo>you can callFoomethods directly onfoo, but you can't do that if you haveCustomRc<Foo>. Correction: you can call methods on theCustomRc<T>if the receiver type isT, but the method receiver (the type ofself) can't beCustomRc<T>in animplblock forT.And yes, it applies to inherent
impls. TheReceivertrait isn't useful for people implementing custom smart pointers because it's also unstable, and isn't available outside the standard library.If I start the RFC process, I'll post here to avoid duplication of effort - please do so too.
Adrian Taylor at 2023-04-28 16:01:04
@adetaylor Thank you for the explanation!
I am happy if you are starting pre-RFC and also happy with pre-discussion blog posts and the like. I would like to help you if and when you start. Would you be ok with that? Is there a Zulip thread I can subscribe to or start?
Amanjeev Sethi at 2023-04-28 17:46:18
Yes! All help very much appreciated. I am not at all promising I'll start the process within the next couple of weeks, so if you get there first that'd be great. If I do anything, it's highly likely to be a blog post about the use cases. There's a small zulip thread here but one of the reasons Manish suggested a blog post was to have something more substantial to post on Zulip to get a bit more discussion going.
The existing Zulip thread is more about the second part of this, implementing the unstable
MethodReceiverpart, rather than stabilizing the existing unstablearbitrary_self_typesstuff. (This is the comment where I try to make the case that they can be done independently!)Adrian Taylor at 2023-04-28 20:03:28
OK I did a thing! Specifically, I wrote the promised blog post summarizing the case for this feature - mostly it's just stealing all the incoming links to this github issue, so thanks to everyone who linked to it.
It's my first time in this rodeo, but to me the selection of use cases (interop with Python, JavaScript, C++, Objective-C) and the need for this in the Linux kernel shows a pretty compelling case.
@amanjeev I mentioned at the end that you and I might be working together on the RFC - I hope that's OK!
Adrian Taylor at 2023-04-29 08:46:06
@adetaylor a few thoughts:
The blog post doesn't cover the concerns of the feature (I see you have a comment above that covers a few though). These definitely need to make it into the RFC.
Speaking of concerns, I'm not seeing anything about "scoping", i.e. being able to add new method resolution to existing types. There is a note about method shadowing, but this is a bit different. I'm not sure how that works currently and the behavior we want.
Jack Huey at 2023-04-29 15:39:03
(i recommend discussing the blog post itself on zulip or something)
Manish Goregaokar at 2023-04-29 15:54:53
@amanjeev I've created a completely blank RFC here and invited you as a collaborator.
Based on Manish's advice it might be a bit premature to start drafting an RFC - we should chat more about things on Zulip with the lang team first. However, I wanted to have somewhere to list all known concerns, to ensure they're eventually discussed in an eventual RFC. I therefore expect this probably to remain in a state where it's just a TODO list and a bunch of concerns, for quite a while. But, feel free to write bits; I may well have a stab at the rationale section relatively soon because I got good feedback on the above blog post and I think I can write a concise version of it.
Adrian Taylor at 2023-05-04 10:50:02
Hey @adetaylor, I'm sorry I didn't really understand what you wrote above, I'm not well-enough versed in
rustcto know such details.What I wanted to discuss was basically that, if we stabilize
arbitary_self_types(close to) as-is, what would be the expected semantics of the following in the future when we addMethodReceiver?struct MyBox<T>(T); impl<T> Deref<Target = T> for MyBox<T> { ... } impl<T> DerefMut for MyBox<T> { ... } unsafe impl<T> MethodReceiver for MyBox<T> { ... }I would expect it be a compile-error, because
MyBox<T>: Derefmeans thatMethodReceiveris already implemented. Or would we expect something different? If so, what would the semantics be?
Maybe, as a library implementer, I'm just confused about your proposal for
MethodReceiver: Why does it need the associatedTargettype and thereceivermethod? In my eyes, the trait could simply be used as:// Crate foo pub struct FooPtr<T>(NonNull<T>); unsafe impl<T> MethodReceiver for FooPtr<T> {} // Does not implement `Deref` and `DerefMut`, since that is undesired for this type // Crate bar struct Bar; impl Bar { // Now possible, because of the `MethodReceiver` implementation. fn method(self: FooPtr<Self>) { ... } }Is it just because the other definition would allow handling
*const Tand*mut Tcleaner? Or is there some other reason?Mads Marquart at 2023-05-05 09:06:54
Maybe, as a library implementer, I'm just confused about your proposal for
MethodReceiver: Why does it need the associatedTargettype and thereceivermethod?Without the
Targettype, how do you know which type(s) to use for the method dispatch?
However, I'm not sure what the purpose of the
receivermethod is: once method lookup is done and we know what method to call, theMethodReceiver::Targettype doesn't matter anymore, and in particular there is no need to obtain an actual*const R::Targetat runtime.Additionally, why restrict the number of
MethodReceiver"coercions" to at most one?// Assuming these: impl<T: ?Sized> MethodReceiver for Cell<T> { type Target = T; } impl<T: ?Sized> MethodReceiver for *const T { type Target = T; } // One should be able to write: struct Foo; impl Foo { fn weird(self: *const Cell<Self>) { ... } }moulins at 2023-05-05 16:44:17
However, I'm not sure what the purpose of the receiver method is: once method lookup is done and we know what method to call, the MethodReceiver::Target type doesn't matter anymore, and in particular there is no need to obtain an actual *const R::Target at runtime.
AIUI, the pointer from the
receivermethod is necessary to get the vtable pointer when calling methods on a trait object. E.g.:trait MyTrait { fn foo(self: Rc<Self>); } impl MyTrait for () { fn foo(self: Rc<Self>) { ... } } let bar: Rc<dyn MyTrait> = Rc::new(()); bar.foo(); // we need to find method at runtime via the vtableImbris at 2023-05-05 21:20:46
AIUI, the pointer from the receiver method is necessary to get the vtable pointer when calling methods on a trait object.
Isn't that handled by the
DispatchFromDynmechanism? Also, I don't think custom method receivers should be required to be object-safe, as this would disallow potentially useful types likeself: RefMut<'_, Self>(which currently cannot be object-safe, as it contains more than one non-ZST field).moulins at 2023-05-06 21:53:58
Hi @madsmtm ,
I would expect it be a compile-error, because
MyBox<T>: Derefmeans thatMethodReceiveris already implemented. Or would we expect something different? If so, what would the semantics be?You're assuming a blanket implementation of
MethodReceiverforDeref, and I wasn't assuming that, so thanks for highlighting this - something to figure out during the pre-RFC process. I will add exactly this question to the blank pre-pre-RFC linked above.Adrian Taylor at 2023-05-10 16:24:26
~~This generic syntax also works under the flag and it would be nice to get it stabilized:~~ Actually, it makes the compiler panic:
impl RandomType { async fn foo(self: &(impl Deref<Target=Self> + Clone + Send + 'static)) { } }This way both
Arc<_>andBox::leakare accepted. TheArcvariant is being suggested to use withtokio::spawn, when trying to create futures referencing self, thoughBox::leakcould also work.Nikita Sokolov at 2023-05-13 14:45:02
I'd like to mention this issue here. The ICE was fixed with an error message, but I'd actually like to see this kind of usage supported:
#![feature(arbitrary_self_types)] use std::ops::Deref; struct Foo(u32); impl Foo { fn get<R: Deref<Target=Self>>(self: R) -> u32 { self.0 } } fn main() { let mut foo = Foo(1); foo.get::<&Foo>(); // Error }The issue being that the self parameter is generic, however, in the call the turbofish operator is used to specify which exact argument type is being used. This doesn't seem to work together with coercing the self argument into the correct reference type though, which I'd argue it should do.
BattyBoopers at 2023-05-24 16:46:15
@adetaylor I just wanted to say thank you for exploring an RFC for this, arbitrary self types provide an elegant solution for the main challenge I would like to resolve before a PyO3 1.0 (i.e. the Python interop you note above).
If there's anything I can do to help you, please ping me!
David Hewitt at 2023-06-17 12:06:46
Thanks also to @Urhengulas and @amanjeev who are working on it too!
Adrian Taylor at 2023-06-19 10:53:40
This issue has become impossible to follow (150 comments, most of them hidden by github).
Tracking issues in general have a tendency to become unmanageable. Please open a dedicated new issue and label it with https://github.com/rust-lang/rust/labels/F-arbitrary_self_types for absolutely any topics you want to discuss or have questions about. See https://github.com/rust-lang/compiler-team/issues/739 for details and discussions on this prospective policy.
Oli Scherer at 2024-04-08 10:21:24
For all those following this tracking issue, note that RFC 3519 ("Arbitrary self types v2") has been accepted and merged:
- https://github.com/rust-lang/rfcs/pull/3519
We'll continue to use this issue to track the progress toward this second version of arbitrary self types.
Thanks to @adetaylor for pushing forward on this important work.
Travis Cross at 2024-05-06 01:06:47
Here's how I plan to approach actually implementing this.
For the immediate future, I'll be working in this branch, rebasing and rewriting history frequently. Bits of that branch will occasionally migrate to PRs which I try to land.
The tricky bit about landing this work is that there are already a cluster of feature gates involved, and we need to adjust them towards this agreed end-state incrementally.
I think the phasing of PRs has to be something like this:
-
Fix the current lifetime elision bugs per https://github.com/rust-lang/rust/issues/117715 and corresponding PR. That got stalled waiting on me as I was dealing with the RFC.
-
Search for potentially conflicting method candidates per the deshadowing part of the RFC. This is the riskiest part - per
the production of errors requires us to interrogate more candidates to look for potential conflicts, so this could have a compile-time performance penalty which we should measure
let's attempt to land this first and see if performance issues show up. If so, it's back to the drawing board.
-
Block generic arbitrary self types. The RFC discussion process suggested that the complexities were too much, and the consensus was to block them with new diagnostics. They're allowed by the current
arbitrary_self_typesfeature gate. We should first of all remove that ability from the existing arbitrary self types code, which is throwaway work, but it will be good to find out early whether this has ecosytem concerns (i.e. anyone relies on it - obviously these are folks relying on the existing unstable feature so we can break them, but we want to know). -
Introduce an
arbitrary_self_types_pointersfeature gate, and move the existing arbitrary self types pointer support to that. In a sense this is temporary throwaway work, since the existing arbitrary self types mechanism will be replaced later in the sequence, but this frees up thearbitrary_self_typesfeature gate to control what we want. Again, we're doing this early to see if this causes unexpected problems for existing crates using the existing unstable feature. -
Rename the old
Receivertrait. Ideally without bikeshedding on the name too much, but we'll see how that goes. Fortunately it should only exist temporarily. This trait should only have been used in the standard library, but doubtless a crater run will yield a surprise or two. -
Land the new
Receivertrait without it doing anything. (I assume it's possible to land things in the standard library even if they're not yet used by the compiler). Include thorough documentation on the restrictions around methods. -
Switch over the method resolution to use the
Receivertrait, if thearbitrary_self_typesfeature is enabled. The main event. -
Add diagnostics for the
!Sizedcase and theNonNulletc. cases. -
Update the Rust reference.
-
Once the dust has settled, a few releases later, look into stabilizing the
arbitrary_self_typesfeature gate.
Help with any of these stages is very much appreciated - as is any wisdom about whether there's a better order to do things, or whether things should be split up into more or fewer PRs - it's my first major Rust change.
Adrian Taylor at 2024-05-21 09:25:55
-
Is there a tracking issue for
DispatchFromDynwhich I could subscribe to? I have a use case for it and I'm trying to figure out if I can make my code more forward-compatible.Martin Habovštiak at 2024-07-09 09:45:03
I don't know if there's an up to date tracking issue, but there were some relatively recent thoughts here.
Adrian Taylor at 2024-07-09 12:04:20
A little update on progress towards the plan outlined in this comment.
The pre-requisite item 1 is in FCP over here. Meanwhile steps 2-8 are in progress in this branch which I'm gradually rewriting and adjusting to put the steps in the order outlined in the plan. At the moment, the first commit in the branch corresponds to step 2, but there's lots more to do to reshuffle all the other commits.
As for step 2 itself... well, it currently causes four tests to fail even though it should be a no-op. I did suspect this would happen. I think in theory all those four failures could be encountered by users with existing regular
rustc, so the next step for me is to try to achieve that and file them as bugs (or, more likely, work out what existing known bugs need to be fixed as a pre-requisite for this step).Adrian Taylor at 2024-07-12 16:01:05
A further update on step 2 in the plan. I managed to eliminate the error cases; they were caused by trait methods shadowing other methods and the RFC specifically says we only want to worry about the case where inherent methods are shadowing other methods, so I was able to modify the code simply not to do that. I raised a PR to test the performance and found some problems which are discussed here - along with next steps. I'm fairly optimistic they can be resolved, but at the expense of making the code more complex. That's what I will try, next time I get back to this (which will be a few weeks).
Adrian Taylor at 2024-07-18 15:53:59
I'm working on a revised version of step 2 here which should minimize performance costs.
Adrian Taylor at 2024-08-02 14:25:55
Hello, friends of Arbitrary Self Types.
Update on general progress
A further update on progress towards the plan above.
I consider step 2 "done". This PR shows, I believe, that it's possible to detect shadowing situations without significant performance impact. The devil might be in the detail (after all, this doesn't actually yet emit any shadowing errors) but for now it's good enough for me to move forward.
Step 3: avoiding "generic" arbitrary self types
During the preparation of the RFC it was broadly felt that we should ban "generic" self types but we didn't really define what "generic" meant, and I didn't pin it down enough.
Some of the commentary:
- https://github.com/rust-lang/rfcs/pull/3519#discussion_r1390267286
- https://github.com/rust-lang/rfcs/pull/3519#discussion_r1435282566
- https://github.com/rust-lang/rfcs/pull/3519#discussion_r1474221554
- A zulip comment
- I've a feeling there's more which I've been unable to find, including the initial impetus to ban generics from arbitrary self types.
It seems to be widely felt that:
impl SomeType { fn m<R: Deref<Target=Self>>(self: R) {} }would be confusing, but (per those comments) it's actually pretty hard to distinguish that from various legitimate cases for arbitrary self types with generic receivers:
impl SomeType { fn method1(self: MyBox<Self, impl Allocator>) {} fn method2<const ID: u64>(self: ListArc<Self, ID>) {} }I played around with different tests here on the
selftype inwfcheck.rsbut none of them yield the right sort of filter of good/bad cases (which is unsurprising since we haven't quite defined what that means, but I thought I might hit inspiration).From those comment threads, the most concrete proposal (from @joshtriplett) is:
just disallow the case where the top-level receiver type itself is not concrete.
I plan to have a crack at that, unless folks have other thoughts. cc @Nadrieril who had opinions here.
If we do this, I might need to revisit the diagnostics mentioned in the bits of RFC removed by this commit.
Adrian Taylor at 2024-08-15 22:17:55
Update: (again relative to the plan in this comment further up)
- Step 3 will hopefully be discussed here, I'm pausing work on that bit to see if a consensus emerges
- Step 4 in progress in this PR
Adrian Taylor at 2024-08-28 08:57:02
Regular status update, relative to the plan outlined before:
- Step 3: still under discussion at #129147: options include:
- do nothing (which seems relatively likely)
- filter out simple generics using the pull request at #130098, which is just about ready to merge if we choose that option
- do something much more complex along the lines of the unfinished #130120.
- Step 4 landed in #129664 and seems to have stuck
- Step 5: PR in progress at #130225
There was some discussion at the Rust for Linux conference about whether we should do steps 5, 6 and 7 as a single atomic PR, but nobody felt strongly so I'll stick with the plan.
Adrian Taylor at 2024-09-11 12:44:25
- Step 3: still under discussion at #129147: options include:
Regular status update relative to the plan: unfortunately not much to report since my previous update:
- Step 3 - no progress on the discussion
- Step 4 - let's assume this has stuck!
- Step 5 - PR has long been approved, waiting for someone with permission to merge it... then I'll start on the next steps after that if it doesn't encounter any problems
Adrian Taylor at 2024-10-22 14:57:59
Another update on progress relative to the plan, as we've got a little further.
- Step 3 - thanks to @wesleywiser I think it's concluded that the best solution here is in this PR which is, as far as I know, ready to merge.
- Step 5 - PR has been merged, which allowed me to move onto...
- Step 6 - just raised a draft PR here. These are the library changes to add the new
Receivertrait. I am still not sure if it's best to land this atomically or with the compiler changes that are in step 7. I'm kind of expecting Rust CI to tell me it's a bad idea to land them separately, but I tend to believe that code review time increases with the square of the lines of code changed, so I'd rather land them separately if I can...
My working branch is also updated and contains most of the remaining stuff I'll need to clean up and get merged eventually.
Adrian Taylor at 2024-10-25 16:36:30
Latest update relative to the plan.
- Step 6 PR has been merged
- Step 7 - the big one - draft PR here
- This has already shown up an unexpected issue with the deshadowing algorithm which I need to do some thinking about, that's the next step!
Adrian Taylor at 2024-11-12 20:53:20
One more update relative to this plan:
- Step 7 PR waiting for review, the concern with the deshadowing algorithm was resolved
- Another version of the PR here with arbitrary self types turned on universally so we can do crater and performance runs, though there are still things I need to fix there
- Working on stage 8 locally, should have a PR soonish, though I don't want to get too far ahead until step 7 has landed, as that's the big complex risky change
Adrian Taylor at 2024-11-29 17:43:00
A periodic update relative to this plan:
- Step 7 PR has been merged: this is the big element so from here on it should mostly be just diagnostics, cleanups, and whatever unknown unknowns exist
- The PR which simulated stabilization of the feature did not reveal any nasty surprises, but it did reveal some crater issues so it's in the queue to rerun crater
- I realized I had been over-eager in adjusting some of the diagnostics so I am rolling back a small bit of the change here
- Step 8: extra diagnostics.
- The easy bit is
Weak,NonNulland raw pointers: PR #134264 - The RFC also says we need diagnostics for the
!Sizedcase. This is proving a bit harder. I've got a bag of horrible diagnostic hacks in this branch some of which hopefully will coalesce into a sensible PR.
- The easy bit is
Adrian Taylor at 2024-12-13 16:13:51
Moved to: https://github.com/rust-lang/rust/issues/134390
<details>I'm looking for advice on the diagnostics around the
Sizedness of a receiver, and I'm hoping @estebank can advise (or of course anyone else @wesleywiser @compiler-errors ).The background (for Esteban):
- This is the tracking issue for "arbitrary self types v2", which allows methods like this:
impl Foo { fn bar(self: MySmartPtr<Self>) {} // note type of self } - The key part of this is that types (such as
MySmartPtr, above) which wish to act as method receivers must implement a new traitReceiver - Before writing the RFC, I happened to make a mistake which I think might be quite common, so in the Diagnostics section of the RFC I proposed adding a diagnostic specifically for this case (the second bullet in that linked section).
The case I want to generate an error for: see this code but, in short, someone implements
ReceiverforTbut notT: ?Sized.Questions. Even partial answers to some questions might point me in the right direction.
Overall approach
- I can quite easily get hold of an unmet obligation for why the type of
selfdoesn't implementReceiver. But how can I determine if some missing?Sizedbound is the problem? a. Re-run the resolution process with some simulated fake sizedSelftype? See if the obligation resolves in that case, and if so, show a diagnostic. b. Simulate that someimpl<T> Receiver for Tblock is actuallyimpl <T: ?Sized> Receiver for T, elsewhere in the program or even in another crate. See if the obligation resolves in that case, and if so, show a diagnostic. c. Suggest "ensure any Receiver implementations cover !Sized" without actually checking that this is the problem. This might give lots of false positives. d. Some kind of heuristics I haven't thought of
Option 1b seems like the option which gives the correct answer. But is it normal in Rust diagnostics to set up simulated worlds with alternative versions of trait blocks, just to answer this sort of question?
-
There are lots of bits of diagnostic code which almost, but not quite, do the right thing, e.g.
suggest_unsized_bound_if_applicable. Can you suggest any pre-existing code which might be helpful here? I am loath to emit an E0277 as well as an E0307, but it's easy enough to do so -
Do you think this case is too niche to bother with a special message at all?
Adrian Taylor at 2024-12-16 17:05:43
- This is the tracking issue for "arbitrary self types v2", which allows methods like this:
Hey @adetaylor: If you're expecting to have collaborative discussion, it's best to split this out of the tracking issue into a separate issue.
Michael Goulet at 2024-12-16 17:08:07
Noted - I'll see if there's any quick resolution here (e.g. "why are you even considering such a niche error case, you fool!") otherwise I'll split it out tomorrow.
Adrian Taylor at 2024-12-16 17:21:44
Another update relative to the plan:
- Step 7: the main event. All landed, but I made some assumptions in the process which are given below.
- Step 8: diagnostics
- The
WeakandNonNulldiagnostics have landed in #134264 - We decided not to land #134384 as it was more complexity than merited
- @cramertj has kindly agreed to figure out the diagnostics for the
!Sizedcase, and more generally, whether we can reuse some of the E0277 machinery to give better diagnostics in cases where theselftype doesn't implementReceiver. This work is being tracked in #134390 and there are some preliminary nudges in this branch: https://github.com/rust-lang/rust/compare/master...adetaylor:rust:arbitrary-self-types-diagnostics
- The
- Step 9: docs update
- Per this comment I'll need to update the Rust reference, dev guide and style guide - I'll be working on those things next. And also the unstable book
Assumptions made during landing the main PR which we should consider before stabilization (if you want to discuss these, please open a new issue instead of discussing here). All of these are related to the deshadowing algorithm, which attempts to spot cases where an outer smart pointer type adds a new method that might "shadow" or override some method in a referent. As a reminder, the complexity here is that the shadower and shadowed might take
selfin different ways, e.g.selfvs&self, so we have to do a bit more searching for method candidates than we previously did.- As noted in this code comment - note you'll have to expand
probe.rsto see it in that diff - and this review comment, we don't notice shadowing cases involving double references like this:// Existing code struct A; impl A { fn foo(self: &&NonNull<A>) {} // note this is by DOUBLE reference } // New code added later impl<T: ?Sized> NonNull<T> { fn foo(&self) {} } - As noted here, the deshadowing algorithm only cares about inherent methods, both in the shadower and shadowee. This is the primary case we are worried about - where an outer smart pointer type adds a method which prevents the inner method being called. Such shadowing errors become less predictable when traits are brought into scope.
- The deshadowing algorithm also ignores the
pick_const_ptr_methodandpick_reborrow_pin_methodparts ofprobe.rs. This means there are (very) niche cases where we won't spot methods being shadowed. Rather than explain that here, I added a test showing these cases in #134509. I think both these cases require other unstable features to be enabled, and even if that weren't the case, I think the possibility of such shadowing cases occurring in the real world is low. (Though perhaps whenpin_ergonomicsis enabled we might want to extend the algorithm to cover that case).
Adrian Taylor at 2024-12-19 12:08:23
A further update on item 9, documentation. All the following PRs are draft and not yet for review, but linking them from here for awareness:
-
Stuff that can land before stabilization
- Unstable doc updates: #134525
-
Stuff that can land only if we stabilize:
- rustc-dev-guide updates: https://github.com/rust-lang/rustc-dev-guide/pull/2168
- Rust reference updates: https://github.com/rust-lang/reference/pull/1699
- Book stuff https://github.com/rust-lang/book/pull/4174
-
Other doc stuff
- I determined that there was no need to update the style guide since there is no new syntax
In the process of preparing these docs, I found a creative, cunning and implausible way to trigger the new deshadowing code without enabling the
arbitrary_self_typesfeature. This wasn't intentional so this is fixed in #134524.Adrian Taylor at 2024-12-19 17:38:00
-
Just for visibility to the team, see my comment on the PR for this in The Book. Short version: not yet sure if it fits in The Book at all, and if it does we’ll need to think about where and how to make it fit! Happy to discuss further… but probably after the holidays!
Chris Krycho at 2024-12-20 23:03:27
Another periodic update:
- #134525 and #134524 landed.
- So, as far as I know, everything is now landed into nightly Rust except the
!Sizeddiagnostics which @cramertj is taking care of in #134390. - If anyone knows anything I've missed, please let me know!
Next steps:
- Wait. Probably a month or so?
- Polish and sort out the doc PRs:
- rustc-dev-guide updates: https://github.com/rust-lang/rustc-dev-guide/pull/2168
- Rust reference updates: https://github.com/rust-lang/reference/pull/1699
- Book stuff https://github.com/rust-lang/book/pull/4174 (per comments from @chriskrycho we might not even want this)
- Prepare a stabilization report which should include:
- The assumptions and questions noted in https://github.com/rust-lang/rust/issues/44874#issuecomment-2553606273
- The theoretical possibility of breaking existing code noted in https://github.com/rust-lang/rust/pull/134524#discussion_r1893214488 (which we don't think can happen in practice because any such code would be pointless)
Adrian Taylor at 2024-12-28 11:13:57
I'm working on a stabilization report here - hopefully will post it tomorrow - meanwhile a draft PR for stabilization is here.
Adrian Taylor at 2025-01-22 14:54:33
@adetaylor we have been discussing the idea of adopting a new stabilization template which is a series of questions:
https://hackmd.io/@nikomatsakis/Sy7wJC9Ikx
I haven't gotten around to opening a PR for this, but I request that you give it a try and tell me how it works for you. It is meant to help ensure we catch common mistakes.
Niko Matsakis at 2025-01-23 16:33:45
OK will do - in that case I withdraw my earlier suggestion that I'd post the stabilization report today - it will take a couple more days to get all that stuff together (I agree it makes sense though)
Adrian Taylor at 2025-01-23 16:40:10
Thanks Adrian.
On Thu, Jan 23, 2025, at 11:40 AM, Adrian Taylor wrote:
OK will do - in that case I withdraw my earlier suggestion that I'd post the stabilization report today - it will take a couple more days to get all that stuff together (I agree it makes sense though)
— Reply to this email directly, view it on GitHub https://github.com/rust-lang/rust/issues/44874#issuecomment-2610358784, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABF4ZT2THCGPDQ7MGC7J6T2MELQFAVCNFSM4D4TO7FKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TENRRGAZTKOBXHA2A. You are receiving this because you were mentioned.Message ID: @.***>
Niko Matsakis at 2025-01-23 17:31:24
This comment formerly contained a stabilization report, which on request I've moved to #135881.
Adrian Taylor at 2025-01-24 12:26:36
@adetaylor: It would be cool if you broke this out into another issue or ideally put the stabilization onto the stabilization PR itself.
Burying into the comment history of a tracking issue seems hard to manage with comments, feedback, concerns, and generally other things here.
Michael Goulet at 2025-01-24 13:26:52
OK - stabilization report moved to #135881.
Adrian Taylor at 2025-01-24 13:33:46
For future traceability: could you link to the various implementations PRs in the top post's "Implementation history" section please?
Nadrieril at 2025-02-04 13:34:29
I don't have permissions to edit the top post, but if somebody could paste this in, that'd be great.
#117967 #129664 #130098 #130225 #132144 #132961 #134262 #134264 #134271 #134509 #134521 #134524 #134525 #136124
Adrian Taylor at 2025-02-05 08:27:52
Seems like arbitrary_self_types lets you do some very surprising things; IMO we should not stabilize until we have a good story for why this is sound.
EDIT: moved
Ralf Jung at 2025-02-07 18:12:00