-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom self types #2362
Custom self types #2362
Conversation
How are conflicting method names resolved? pub struct Ptr<T>(T);
impl<T> Deref<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> Ptr<T> {
pub fn foo(&self) {}
}
pub struct Bar;
impl Bar {
fn foo(self: &Ptr<Self>) {}
} |
|
||
However, we also feel that `CoerceUnsized` is not ready to stabilize without | ||
further consideration of the trade offs. For that reason, defining your own | ||
arbitrary method receivers may not be stabilized as quickly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this division makes sense. It shouldn't even require an extra feature gate, since CoerceUnsized
is already unstable, correct? In other words, the delayed stabilization described here doesn't require delaying stabilization of any part of this RFC-- it just specifies that CoerceUnsized
, an existing unstable feature, will remain unstable.
How will conflicts between methods on e.g. // In an upstream crate
impl<T> CleverPtr<T> {
fn foo(self) { ... }
}
// In a downstream crate:
struct MyType;
impl MyType {
fn foo(self: CleverPtr<MyType>) { ... }
}
fn main() {
let x = CleverPtr::new(MyType);
x.foo(); // ERROR: conflicting methods.
// What do I write to solve this? UFCs doesn't help:
<CleverPtr<MyType>>::foo(x) // Which method is this?
} Edit: whoops, looks like @sfackler beat me to it :) |
@sfackler Good question! Its already implemented, so they must be resolved somehow. Let's check... (playpen) It's currently a name collision. (@cramertj I think your question is the same.) |
Ah cool, and you can disambiguate with |
Ah, I see-- the type in UFCs is the type of the (In other words, |
@sfackler Right! It seems analogous to two traits containing the same method name. I could see us introducing a hierarchy some day so that impls defined where |
# Summary | ||
[summary]: #summary | ||
|
||
Allow types that implement `Deref` targeting `Self` to be the receiver of a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is clearer:
Allow types that implement
Deref<Target = Self>
...
feature. | ||
|
||
This feature is increasingly relevant because of the role of special pointer | ||
types to constraint self-referential types, such as generators containing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: s/constraint/constrain
[guide-level-explanation]: #guide-level-explanation | ||
|
||
When declaring a method, users can declare the type of the `self` receiver to | ||
be any type `T` where `T: Deref<Target = Self>`. Shorthand exists, so that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Shorthand exists/Shorthands exist/
fn by_rc(self: Rc<Self>); | ||
fn by_arc(self: Arc<Self>); | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about HKTs in the future (assuming we get HKTs which we might not)?
Consider:
trait Functor<Self : * -> *> { // strawman syntax for encoding the kind of Self : * -> *
fn fmap<A, B, F>(self: Self<A>, mapper: F) -> Self<B>
where F: Fn(A) -> B;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have a bound where Self<A>
derefs to Self
, I don't see why not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But Self
is not of the kind of values, it has kind * -> *
, so you shouldn't be able to construct a value of Self
at all in this case (hence, you can't deref Self<A>
to Self
).
I guess you could have a separate rule for higher kinded types and say that this rule only applies to *
-kinded types.
|
||
To support object-safety, we must have a way of obtaining a vtable from the | ||
reference type, and then passing the correct receiver type to the function in | ||
the vtable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered interactions with multiple trait bounds in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's an interaction. This RFC doesn't introduce any constraint that doesn't already exist today.
Two sortof-related questions:
|
@glaebhoerl I don't really understand your questions. Its true that object safety requires impl String {
fn this_is_a_string_method(self: i32) { }
}
// These two expressions do the same thing:
0.this_is_a_string_method()
String::this_is_a_string_method(0) I don't see how |
(I think both of them boil down to trying to clarify for myself why symmetries which typically manifest -- between the requirements of free |
At least when I wrote the proposal, For this reason, I'm not sure how useful is that property, because adding a new dependency can worsen type inference, but it felt like a useful thing to keep. |
When I last looked on this, I remember that the current checking of However,
|
I assume that |
Is it possible to allow trait object as the type of trait Bar {
fn baz(self: &dyn Deref<Target=Self>);
} |
And I have another question which might be off topic. Does this RFC solve the issue rust-lang/rust#28796? My understanding is that it is possible to allow |
cc @mikeyhew who will hopefully be spearheading the implementation work on this :) |
With respect to the trait object rule, there's something a bit implicit in there I want to better understand. The RFC states:
It seems like we would require a "HKT-like" feature here to decide what But, for example, I imagine we don't want anyone to write this: trait Foo {
fn method(self: Deref1<Self, Self>) { .. }
}
struct Deref1<A: ?Sized, B: ?Sized> { }
impl<A: ?Sized, B: ?Sized> Deref for Deref1<A,B> {
type Target = A;
} |
Similarly, the way the RFC is written, it is fine (in inherent impls, anyway) to have a receiver type derefs to
|
I think it'd be useful to dig a bit more into how method dispatch on a trait object will work. Maybe I'm just slow, but the text in the RFC is a bit terse. =) So, let me try to spell it out for my own edification. We start out with a trait Trait {
fn foo(self: Rc<Self>);
} We consider
Then, at runtime, we can deref
Assuming these docs are accurate (I should check the code), then it seems like we already know that actually we can just strip the vtable from the @arielb1 I'm curious what constraints you think are insufficient. (If we were to generalize |
@nikomatsakis You're not slow, I'm just uncertainly cribbing from the notes that you and @mikeyhew gave me. :) Everything you've said seems accurate. I think we should restrict the self type in traits to having the |
I agree with this notion. I'm wary of stabilizing the way that we determine if a I don't think that the
It doesn't have to be a newtype'd pointer, it just has to have a field that is a pointer (it can have other fields too). |
This is why the `CoerceUnsized` bound is necessary for object-safe receiver | ||
types. Using the type ID stored in the vtable, we can downcast the `Self` type | ||
to the correct concrete type to pass to the function pointer for the method | ||
we're calling, effectively reversing the unsizing coercion. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that "type ID" and "downcast" is the right terminology here? My understanding was that the compiler looks up the method in the vtable, coerces the Rc<Foo>
to Rc<WhateverTypeThatImplementsFoo>
without actually knowing what that type is, and calls the method using the coerced Rc
as the self
argument
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right.
However, I believe those fields have to be phantom-data -- or at least zero-sized? That's what the description says, anyway. |
be implemented. Given a reference type `Ptr<dyn Trait>`, the compiler must be | ||
able to prove that `T: Unsize<dyn Trait>` implies `Ptr<T>: | ||
CoerceUnsized<Ptr<dyn Trait>>`. If the compiler can prove this, methods with | ||
these receivers are object safe (how they object safe conversion is implemented |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: typo they -> the
Something not covered in the RFC and I don't recall seeing discussed is lifetime elision, in the pub fn get_pin_mut(self: PinMut<'a, Self>) -> PinMut<'a, Si> If arbitrary self types supported the same elision as normal references this could be: pub fn get_pin_mut(self: PinMut<Self>) -> PinMut<'_, Si> But that seems a bit terse, and loses out on the "all types have either pub fn get_pin_mut(self: PinMut<'_, Self>) -> PinMut<'_, Si> to remove the need to give a name to this very transient lifetime. (I found this because Clippy's |
@Nemo157 I think going with |
so.... will this be merged? |
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. Right now, in the implementation, we are using a new, unstable trait called Another change that I'm mostly in favour of, is calling the |
Nominating this for @rust-lang/lang -- I think it just got overlooked? AFAIK we stabilized some subset of this functionality! |
We discussed this briefly in the @rust-lang/lang meeting today and decided to postpone. I was mistaken in saying that we stabilized the functionality in here -- in truth, what we stabilized was extending the set of "recognized self types" to include (Note that the RFC content would obviously be a useful starting point for future discussion when we do revisit the topic.) @rfcbot postpone |
Team member @nikomatsakis has proposed to postpone this. The next step is review by the rest of the tagged team members: No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
For the record, we stabilized |
That’s is a really unfortunate state to leave things in indefinitely. |
This is not intended as the end state of things. We should get back to
I believe we agree overall. |
Are there interesting complexities in the mutable cases like |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period, with a disposition to postpone, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. The RFC is now postponed. |
Extends support for new self types, such as
Rc<Self>
andArc<Self>
.Rendered