Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upCustom self types #2362
Conversation
withoutboats
added some commits
Feb 20, 2018
Centril
added
the
T-lang
label
Mar 14, 2018
This comment has been minimized.
This comment has been minimized.
|
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>) {}
} |
cramertj
reviewed
Mar 14, 2018
|
|
||
| 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. |
This comment has been minimized.
This comment has been minimized.
cramertj
Mar 14, 2018
Member
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.
This comment has been minimized.
This comment has been minimized.
|
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 :) |
This comment has been minimized.
This comment has been minimized.
|
@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.) |
This comment has been minimized.
This comment has been minimized.
|
Ah cool, and you can disambiguate with |
This comment has been minimized.
This comment has been minimized.
|
Ah, I see-- the type in UFCs is the type of the (In other words, |
This comment has been minimized.
This comment has been minimized.
|
@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 |
Centril
reviewed
Mar 14, 2018
| # Summary | ||
| [summary]: #summary | ||
|
|
||
| Allow types that implement `Deref` targeting `Self` to be the receiver of a |
This comment has been minimized.
This comment has been minimized.
Centril
Mar 14, 2018
Contributor
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 |
This comment has been minimized.
This comment has been minimized.
| [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 |
This comment has been minimized.
This comment has been minimized.
| fn by_rc(self: Rc<Self>); | ||
| fn by_arc(self: Arc<Self>); | ||
| } | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
Centril
Mar 14, 2018
•
Contributor
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;
}
This comment has been minimized.
This comment has been minimized.
arielb1
Mar 14, 2018
Contributor
If we have a bound where Self<A> derefs to Self, I don't see why not.
This comment has been minimized.
This comment has been minimized.
Centril
Mar 15, 2018
•
Contributor
But Self is not of the kind of values, it has kind * -> *, so you shouldn't be able to construct a value of Selfat 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. |
This comment has been minimized.
This comment has been minimized.
Centril
Mar 14, 2018
Contributor
Have you considered interactions with multiple trait bounds in the future?
This comment has been minimized.
This comment has been minimized.
withoutboats
Mar 14, 2018
Author
Contributor
I don't think there's an interaction. This RFC doesn't introduce any constraint that doesn't already exist today.
This comment has been minimized.
This comment has been minimized.
|
Two sortof-related questions:
|
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
|
(I think both of them boil down to trying to clarify for myself why symmetries which typically manifest -- between the requirements of free |
This comment has been minimized.
This comment has been minimized.
|
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. |
This comment has been minimized.
This comment has been minimized.
|
When I last looked on this, I remember that the current checking of However,
|
This comment has been minimized.
This comment has been minimized.
|
I assume that |
This comment has been minimized.
This comment has been minimized.
|
Is it possible to allow trait object as the type of trait Bar {
fn baz(self: &dyn Deref<Target=Self>);
} |
This comment has been minimized.
This comment has been minimized.
|
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 |
aturon
assigned
withoutboats
Mar 15, 2018
This comment has been minimized.
This comment has been minimized.
|
cc @mikeyhew who will hopefully be spearheading the implementation work on this :) |
This comment has been minimized.
This comment has been minimized.
|
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;
} |
This comment has been minimized.
This comment has been minimized.
|
Similarly, the way the RFC is written, it is fine (in inherent impls, anyway) to have a receiver type derefs to
|
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
|
@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 |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Mar 17, 2018
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). |
mikeyhew
reviewed
Mar 17, 2018
| 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. |
This comment has been minimized.
This comment has been minimized.
mikeyhew
Mar 17, 2018
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
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
However, I believe those fields have to be phantom-data -- or at least zero-sized? That's what the description says, anyway. |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Mar 17, 2018
•
|
@nikomatsakis that description must be out of date, then. Here is an example of a pointer with an additional, non-zero-sized field: https://play.rust-lang.org/?gist=4df5fa3c12db2d4545edd283ae76e76a&version=nightly |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Mar 17, 2018
•
|
@clarcharr
Sorry, I don't understand the question.
That actually works right now with the EDIT: updated the link, thanks @glaebhoerl!
That is unrelated. The issue is that |
This comment has been minimized.
This comment has been minimized.
|
(@mikeyhew you accidentally linked the same playpen again as in your previous comment) |
This comment has been minimized.
This comment has been minimized.
Agreed! My vision is that we stabilize using the object safe method receivers in std immediately as a generalization of |
This comment has been minimized.
This comment has been minimized.
I had a feeling that might be the case.
I agree, but I think that the right solution feels like it might still be in this general direction: that is, basically we are saying that |
This comment has been minimized.
This comment has been minimized.
The current definition of That's it, you can write code such as the following (I was not aware of this particular example back then, only suspected that it existed): #![feature(dyn_trait, coerce_unsized)]
use std::fmt;
use std::ops::CoerceUnsized;
trait Xyz {
type Output: ?Sized;
}
impl<T> Xyz for T {
type Output = u32;
}
impl Xyz for dyn fmt::Debug {
type Output = fmt::Debug;
}
struct Foo<T: Xyz + ?Sized>(Box<<T as Xyz>::Output>);
impl<T> CoerceUnsized<Foo<dyn fmt::Debug>> for Foo<T> {
}
fn main() {
let x: Foo<()> = Foo(Box::new(2));
let x: Foo<dyn fmt::Debug> = x;
println!("{:?}", x.0);
}This type-checks, and the only way this works is if the vtable in However, this code is silly and ICEs, so the better option is probably to fix |
This comment has been minimized.
This comment has been minimized.
|
So on my second look, this looks more like a poor definition of the unstable |
This comment has been minimized.
This comment has been minimized.
newpavlov
commented
Mar 20, 2018
•
|
Will this RFC bring us closer to self-consuming object safe trait methods? Ideally I would like to be able to write the following code: trait Foo {
fn consume(self: Consumed<Self>) {
// `Consumed<Self>` implements `DerefMut<Self>`
// but to create it you'll have to move your data into it
}
}
// `T` is a concrete type which implements `Foo`
fn bar1(val: T) {
// this line desugars into `Consumed::new(val).consume()`
val.consume()
}
fn bar2(val: Box<Foo>) {
// `val` can not be used after this line
val.consume()
} |
This comment has been minimized.
This comment has been minimized.
|
@newpavlov No, this RFC is an orthogonal feature. Self-consuming object-safe methods dependent upon being able to generate a vtable which consumes |
This comment has been minimized.
This comment has been minimized.
gralpli
commented
Mar 21, 2018
|
Does this work for |
This comment has been minimized.
This comment has been minimized.
MSleepyPanda
commented
Mar 21, 2018
•
|
@gralpli Probably not, because
Edit: |
This comment has been minimized.
This comment has been minimized.
Thanks for the example! We should indeed try to write out the |
This comment has been minimized.
This comment has been minimized.
bill-myers
commented
Apr 2, 2018
•
|
Why does this need Deref at all? If we have a Foo<dyn Bar>, doesn't that type already include a vtable for trait Bar? If the concern is that we don't want to add methods to random types, then we could support not having Deref, but only allow UFCS to call the method in that case. |
Nemo157
referenced this pull request
Apr 7, 2018
Open
Tracking issue for RFC 2033: Experimentally add coroutines to Rust #43122
mikeyhew
referenced this pull request
May 12, 2018
Open
Tracking issue for `arbitrary_self_types` #44874
ebfull
referenced this pull request
May 31, 2018
Closed
RFC: add futures and task system to libcore #2418
parasyte
reviewed
Jun 8, 2018
| 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 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
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 |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Jul 21, 2018
|
@Nemo157 I think going with |
Nemo157
referenced this pull request
Jul 24, 2018
Open
arbitrary_self_types don't support lifetime elision #52675
This comment has been minimized.
This comment has been minimized.
Laaas
commented
Aug 30, 2018
|
so.... will this be merged? |
This comment has been minimized.
This comment has been minimized.
mikeyhew
commented
Aug 30, 2018
|
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 |
withoutboats commentedMar 14, 2018
•
edited by Centril
Extends support for new self types, such as
Rc<Self>andArc<Self>.Rendered