-
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
RFC: deref coercions #241
RFC: deref coercions #241
Conversation
cc @nick29581 @nikomatsakis |
Of course, method dispatch can implicitly execute code via `Deref`. But `Deref` | ||
is a pretty specialized tool: | ||
|
||
* Each type `T` can only deref to *one* other type. |
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.
Is this a formal restriction or an informal one? AFAIK it's currently possible to do something like
trait Foo { ... }
impl Foo for int { ... }
impl Foo for uint { ... }
struct Bar;
impl<T> Deref<T> for Bar where T: Foo { ... }
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.
Once associated types land, we'd make T
an associated type, which would force it to be uniquely determined by the Self
type.
That restriction is necessary for the coercion algorithm being proposed to work.
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.
@sfackler Updated the RFC to clarify.
This is a key difference from the | ||
[cross-borrowing RFC](https://github.com/rust-lang/rfcs/pull/226). | ||
|
||
### Limit implicitly execution of arbitrary code |
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: should be "implicit execution of" or "implicitly executed"
Did any of the examples show an instance of the second bullet's coercion, namely: "From &mut T to &U when T: Deref" ? I ask because that is a case where something that looks like an ownership transfer (of the It could be that the trade off here is in the RFC's favor. I just want to make sure we show an example justifying the second bullet in particular. |
```rust | ||
let v = vec![0u8, 1, 2]; | ||
foo(v); // is v moved here? | ||
bar(v); // is v still available? |
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.
My basic counter-argument to this is that the compiler tells you exactly this - if this is working code, then you know for sure that v
is available in the call to bar
so, you don't benefit from having to write &
.
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 a fair assumption for experienced Rust developers, but this is a large disadvantage for people new to Rust, who will just be extremely confused when two function calls that look like they use v in exactly the same way actually use it completely differently.
Actually I just remembered: of course there are other cases where passing a Still, adding the requested example would benefit the RFC presentation. |
The design satisfies both of the principles laid out in the Motivation: | ||
|
||
* It does not introduce implicit borrows of owned data, since it only applies to | ||
already-borrowed data. |
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.
This is really nice!
@pnkfelix actually, coercing |
@nick29581 it seems like the key question is whether, indeed, programmers need to be very aware of the levels of indirection. I contend that they do not. In terms of capabilities, there is virtually no difference between a Speaking personally, I find accounting for the precise amount of indirection to be tedious and I don't think it adds much information. It's not a big deal, but converting from However, when we used to have cross-borrowing, I did find that confusing. I remember distinctly being surprised to see vectors that looked like they were being moved, but were in fact being (implicitly) borrowed, and also being surprised that a |
@nikomatsakis yes, totally agree it boils down to whether programmers need to be concerned about the level of indirection. I could be persuaded. My experience is mostly from C++ where it is deadly important to know. I agree that at first blush I ignore the I want to believe that we can not worry about pointers and only about ownership, it seems a much more attractive model. But my experience in the past has always been that if I don't know precisely what is going on, there is confusion, and that makes debugging harder. I guess I think of borrowing, deep down, as just a pointer to something, so trying to not think of pointers is difficult for me. I'll try and think only of ownership and borrowing for a while and see if I can warm to the idea of reasoning without pointers :-) |
On Tue, Sep 16, 2014 at 04:31:35AM -0700, Nick Cameron wrote:
That's interesting. I don't think I've ever had a bug in Rust code The closest example I can come up with is that I have seen errors |
I am still not decided on whether this is the best approach, but a couple of thoughts if it is:
and so forth. I think this gives similar results to this RFC, but also works for I believe the borrow operator has the following advantages over a cross-borrow-deref coercion:
Does this sound like a sensible alternative or is it silly? If it is the former I can write up an RFC so we have a place to discuss, rather than doing so here. |
aba44ab
to
896e453
Compare
I tend to agree; we could drop
Yes, though it depends on what implements
(details elided)
It's an intriguing idea, but seems problematic. Consider cases like the following: fn wants_vec_ref(v: &mut Vec<u8>) { ... }
fn has_vec(v: Vec<u8>) {
wants_vec_ref(&mut v)
} If (Just wanted to jot that down for now -- more later.) |
@aturon @nick29581, another alternative would be making coercions explicit, but not too explicit. Some ideas here: Semi-explicit coercion control with |
On Tue, Sep 16, 2014 at 01:43:23PM -0700, Nick Cameron wrote:
Why does the current RFC not work for Regarding the slice notation, I agree that |
@Thiez Syntax like |
@Valloric And syntax like If you're going to learn a new language, you have to learn the syntax. That is not a bad thing. And while the syntax certainly shouldn't be made more complex that it needs to be, I don't think making the semantics of |
+1
|
+1 |
There is an implementation now, and there seems to be a lot of positive sentiment for this change, should we push forward with it? @aturon are there any open questions you're aware of? (I haven't re-read the whole conversation here, but I will if we are going to move on to this. But I'm mostly checking you don't have any new concerns which aren't already here). I'm still a little uneasy about the 'searching' aspect of the coercion, but I think it is worth it for the ergonomic benefit. |
The |
Note that this is one reason why the slice syntax |
Yes, we're ready to move forward with this; this way we can gain some experience before the beta. |
Note: this RFC has now been merged, after some significant digestion on all our parts :-) I won't recap the motivation/arguments here, which are well-represented in the RFC and thread. However, I will note that an implementation is nearly ready to land, and so we should have time to gain some experience here before shipping beta. |
…ce deref functionality (rust-lang/rfcs#241).
Add a select combinator for streams
Add the following coercions:
&T
to&U
whenT: Deref<U>
.&mut T
to&U
whenT: Deref<U>
.&mut T
to&mut U
whenT: DerefMut<U>
These coercions eliminate the need for "cross-borrowing" (things like
&**v
) and calls toas_slice
.Rendered