Re: [rust-dev] Removing some autoref magic
I found this posthttp://smallcultfollowing.com/babysteps/blog/2012/10/01/moves-based-on-type/by Niko, and it seems that at the time everyone agreed that implicit moving actually makes things more clear. Why is this different now, only a year later? :-) Ok, but serieosly, I allow that this is different for function arguments vs regular code in the function body. So maybe the right thing to do *is *to stop moving function parameters by default? On Tue, Nov 19, 2013 at 5:35 PM, Vadim vadi...@gmail.com wrote: If the function is not going to mutate it, I don't really care if it's by-ref or by-value. That's why I said const T, not T. Well, except for the case when a function caches a reference to something which I later mutate. But borrow checking will prevent that. I would also be totally fine with letting the compiler figure out how immutable arguments are passed (based on data type size, existence of destructors, and whatnot). This might not be the best idea for binary compatibility of public APIs, but I don't see why it can't be done with crate-private functions. On Tue, Nov 19, 2013 at 5:07 PM, Ziad Hatahet hata...@gmail.com wrote: Personally I would prefer if in Rust worked similar to const T in c++ In that case, you would not be able to tell whether a function argument was passed by value or by reference. I actually like this feature about Rust (C# has it too with the `ref` keyword). -- Ziad On Tue, Nov 19, 2013 at 4:54 PM, Vadim vadi...@gmail.com wrote: So why did Rust adopt auto-moving instead of explicit moving? If the second example had to be written as foo(move a) there would be no confusion. The and the third example should arguably be sort(mut a.as_mut_slice()). Personally I would prefer if in Rust worked similar to const T in c++ (i.e. for most intents and purposes you can treat a reference as a value), otherwise half of the arguments on each function call will have to be adorned with ampersands. Can we have it such that foo(a) would be guaranteed to not mutate or move a and require mut or move prefix otherwise? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 20 November 2013 02:07, Ziad Hatahet hata...@gmail.com wrote: Personally I would prefer if in Rust worked similar to const T in c++ In that case, you would not be able to tell whether a function argument was passed by value or by reference. I actually like this feature about Rust (C# has it too with the `ref` keyword). In the safe code there is no difference if the value is passed via a copy or as T. Also as I learned recently the Rust compiler already optimizes T parameters into T for small-sized T at --opt-level=3. So effectively T parameter became a notation for the most efficient passing of arguments of type T. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On Wed, Nov 20, 2013 at 12:30:59AM -0800, Vadim wrote: I found this posthttp://smallcultfollowing.com/babysteps/blog/2012/10/01/moves-based-on-type/by Niko, and it seems that at the time everyone agreed that implicit moving actually makes things more clear. Why is this different now, only a year later? :-) I stand by the arguments I made in that post. My experience at least has been that implicit moves have been much more pleasant to work with than explicit ones, and the language is simpler as well. Niko ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
First, I am speaking for myself and not my employer. The current proposal is to remove all autoref except for function invocations and indexing operations. The method of creating T from ~T would be `let foo: T = foo` or `*foo`. Vectors and strings can't currently benefit from the `*foo` syntax, but they will hopefully be able to do such once DST lands. In the meantime coercion via type ascription will work and they also have `as_slice` methods. I am against this proposal. As I understand, we will still keep autoref, autoborrow, etc. in method calls. So I don't think inconsistency argument holds. Neither does local readability argument. For local readability, which I agree is important, we have naming conventions naming methods taking mut self with mut_* and methods taking by-value self with move_*. (Methods normally take self.) And with better tooling, function signature should be locally accessible to the call site. I am not in the camp of less compiler magic. Some people seem to argue for removal of automatic coercions even in method calls, and I am very much against that idea. Someone's magic is other's convenience. As for #10504, perceived asymmetry seems to originate from the confusion of value and reference. As for inconsistency of automatic coercions, I propose we add automatic coercions to return expression, and any other places as we discover other missing places. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
Have you considered making deref the default instead and requiring moves to use an explicit move keyword? Basically, from this hypothetical syntax to current one: - x = x - mut x = mut x - move x = x One could even make the implicit in parameter types in function declarations unless the move keyword is used, so we would have (new = old syntax): - fn(self, ...) = fn(self, ...) - fn(mut self, ...) = fn(mut self, ...) - fn(move self, ...) = fn(self, ...) Regarding owned pointers, one could introduce this syntax: let *x = func_that_gives_owned_pointer() which makes x a variable of type T referring to the contents of the owned box, and thus derefed to when passed to a function as above without having to write *x in the function call. One could then add a new #x or similar syntax that would get back to the owned pointer. Not sure if all this is a good idea, just throwing it out there. I actually think keeping autoref/autoderef is probably better. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 11/20/2013 02:51 PM, Bill Myers wrote: Have you considered making deref the default instead and requiring moves to use an explicit move keyword? Basically, from this hypothetical syntax to current one: - x = x - mut x = mut x - move x = x One could even make the implicit in parameter types in function declarations unless the move keyword is used, so we would have (new = old syntax): - fn(self, ...) = fn(self, ...) - fn(mut self, ...) = fn(mut self, ...) - fn(move self, ...) = fn(self, ...) I like this proposal, because afaik it matches the common use case. Also, it would make things easier to learn, understand, and use, for people not used to move semantics (even less to it as default / implicit case). I do struggle with move; making it explicit would help (also when reading it other people's code). Denis ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 11/19/13 9:42 PM, Tommi wrote: Our problem is that, given let arg: ~A;, seeing only foo(arg) in code doesn't tell us whether arg is moved or borrowed. The proposed solution is that auto-borrowing in that context would be deprecated and thus would require an explicit borrowing: foo(*arg). Now, given that it seems that the upcoming UFCS would simply re-write arg.foo() to foo(arg), it would mean that seeing only arg.foo() in code doesn't tell us whether arg is moved or borrowed. Thus, the proposed solution would fix only half of the problem. Again, I don't see this as an argument against the proposal. The dot operator is always magical. Magic on one place doesn't justify magic everywhere. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 11/20/13 5:51 AM, Bill Myers wrote: Have you considered making deref the default instead and requiring moves to use an explicit move keyword? Tried it a year ago. It was incredibly noisy. I don't want to go back to that world. We saw things like: let move x = move foo(move y); Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 11/20/13 6:25 AM, spir wrote: I like this proposal, because afaik it matches the common use case. Also, it would make things easier to learn, understand, and use, for people not used to move semantics (even less to it as default / implicit case). I do struggle with move; making it explicit would help (also when reading it other people's code). You have to know moves to use Rust. The noise didn't aid understanding a year ago, and I don't think it will now. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 11/20/13 5:05 AM, Sanghyeon Seo wrote: I am against this proposal. As I understand, we will still keep autoref, autoborrow, etc. in method calls. So I don't think inconsistency argument holds. Neither does local readability argument. It is a good point that a true emphasis on consistency between methods and functions would have us perform auto-deref and auto-ref on all argument positions. Then the LHS of `.` and arguments would truly be treated the same way, which dovetails with UFCS pretty nicely. I'm pretty nervous about performing auto-ref on function arguments, though, *especially* `mut` auto-ref. For some reason I expect that this: let mut a = ~[x]; a.push(hi); may mutate `a`, while: let mut a = ~[y]; push(a, b); will *not* mutate `a`. Do others have the same expectation? Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On Wed, Nov 20, 2013 at 11:37 AM, Patrick Walton pcwal...@mozilla.com wrote: On 11/20/13 5:05 AM, Sanghyeon Seo wrote: I am against this proposal. As I understand, we will still keep autoref, autoborrow, etc. in method calls. So I don't think inconsistency argument holds. Neither does local readability argument. I'm pretty nervous about performing auto-ref on function arguments, though, *especially* `mut` auto-ref. For some reason I expect that this: let mut a = ~[x]; a.push(hi); may mutate `a`, while: let mut a = ~[y]; push(a, b); will *not* mutate `a`. Do others have the same expectation? I do, and I agree a lot with Niko's arguments from the meeting. I agree that * is a bit ugly, but I prefer the ugly syntax to the ambiguity. I don't find Kevin's argument particularly convincing; those changes can make your code fail to compile, but *not* to transparently behave differently (simply by changing the types of the args). I'm in favor of the proposal. What we have now isn't that great and moving to an alternative is good, even if it's not the best. We can move to something better later. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
One caveat: this e-mail made it sound as if the proposal were universally favored at the meeting, but I at least feel conflicted, though I may not have that clear. I started writing a response but it got long, so I moved it to a blog post: http://smallcultfollowing.com/babysteps/blog/2013/11/20/parameter-coercion-in-rust/ I'll just paste the text inline here, starting with the conclusion: ## What should we do? I think we ought to experiment. It's not too hard to whip up a branch with the two alternatives and work through the implications. I'm currently doing the same with more stringent rules around operator overloading, and the experience has been instructive -- I haven't yet decided if it's acceptable or not. But that's a topic for another post. ## Historical background In the interest of clarity, I wanted to briefly explain some terminology and precisely what the rules *are*. I refer to autoref as the addition of an implicit ``: so converting from `T` to `T`, in terms of the type. Autoderef is the addition of an implicit `*`: converting from `T`, `~T`, etc to `T`. Finally, autoborrow is the addition of both a `` and a `*`, which effectively converts from `~T`, `T` etc to `T`. Autoslice is the conversion from `~[..]` and `[...]` to `[...]` -- if we had a DST-based system, autoslice and autoborrow would be the same thing, but in today's world they are not, and in fact there is no explicit syntax for slicing. Currently we apply implicit transformations in the following circumstances: - For method calls and field accesses (the `.` operator) and indexing (`[]`), we will autoderef, autoref, and autoslice the receiver. We're pretty aggressive here, happily autoderefing through many layers of indirection. This means that, in the extreme case, we can convert from some nested pointer type like `@T` to a type like `T`. - For parameter passing, local variable initializers with a declared type, and struct field initializers, we apply *coercion*. This is a more limited set of transformations. We could and probably should apply coercion in a *somewhat* wider set of contexts, notably return expressions. Currently we are specifically referring to what the *coercion* rules ought to be. Nobody is proposing changing the method lookup behavior. Furthermore, nobody is proposing removing coercion altogether, just changing the set of coercions we apply. ### Current coercion rules The current coercion rules are as follows: - Autoborrow from `~T` etc to `T` or `mut T`. - Reborrowing from `'a T` to `'b T`, `'a mut T` to `'b mut T`, which is a special case. See discussion below. - Autoslice from `~[T]`, `[T]`, `[T, ..N]` etc to `[T]` - Convert from `T` to `*T` - Convert from a bare fn type `fn(A) - R` to a closure type `|A| - R` In addition, I believe that we *should* have the rule that we will convert from a pointer type `T` to an object type `Trait` where `T:Trait`. I have found that making widespread but non-uniform use of object types without this rule requires a lot of explicit and verbose casting. ### Slicing If we had DST, then slicing and borrowing are the same thing. Without DST, there is in fact no explicit way to slice a vector type like `~[T]` or `[T, ..N]` into a slice `[N]`. You can call the `slice()` method, but that in fact relies on the fact that we will autoslice a method receiver. For vectors like `~[N]` and so on, we could implement `slice` methods manually, but fixed-length vectors like `[T, ..N]` are particularly troublesome because there is no way to do such a thing. ### Reborrowing One of the less obvious but more important coercions is what I call *reborrowing*, though it's really a special case of autoborrow. The idea here is that when we see a parameter of type `'a T` or `'a mut T` we always reborrow it, effectively converting to `'b T` or `'b mut T`. While both are borrowed pointers, the reborrowed version has a different (generally shorter) lifetime. Let me give an example where this becomes important: fn update(x: mut int) { *x += 1; } fn update_twice(x: mut int) { update(x); update(x); } In fact, thanks to auto-borrowing, the second function is implicitly transformed to: fn update_twice(x: mut int) { update(mut *x); update(mut *x); } This is needed because `mut` pointers are *affine*, meaning that otherwise the first call to `update(x)` would move the pointer `x` into the callee, leading to an error during the second call. The reborrowing however means that we are in fact not moving `x` but rather a temporary pointer (let's call it `y`). So long as `y` exists, access to `x` is disabled, so this is very similar to giving `x` away. However, lifetime inference will find that the lifetime of this temporary pointer `y` is limited to the first call to `update` itself, and so after the call access to `x` will be restored. The borrow checker rules permit reborrowing under the
Re: [rust-dev] Removing some autoref magic
may mutate `a`, while: let mut a = ~[y]; push(a, b); will *not* mutate `a`. Do others have the same expectation? But isn't there a missing context there ? That 'a' does not necessarily have to be mutable globally ? But could be constrained... only in the defined functions, or...traits...or ? -- -Thad +ThadGuidry https://www.google.com/+ThadGuidry Thad on LinkedIn http://www.linkedin.com/in/thadguidry/ ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
I'm wondering something. Have the Rust developers considered the possibility of using references instead of pointers? It seems to me that this would eliminate a lot of the need for autoderef. Now I'm not well-equipped to talk about Rust (some of the rules I am totally ignorant about, e.g. I know rust has a ref keyword but the tutorial does not mention it) so let me talk about C++ instead. C++, of course, has references, which are exactly like pointers except they are always implicitly dereferenced: int y = 123; int* x1 = y; // *x1 == y x1 == y int x2 = y; // x2 == y x2 == y In fact, with one small tweak, C++ could have entirely eliminated the need for pointers (except array pointers), and references could have been used for everything except arrays. The reason references cannot replace pointers is that the pointer--the true identity of a reference--is immutable. But that's easily fixable though, by defining x2 as an lvalue: x1 = z; x2 = z; // Aha! We've changed the pointer inside the reference If the second line were legal, then references could do everything that pointers could do; in that case I think that, for consistency, people would have eventually standardized on using references for everything that doesn't require pointer arithmetic (i.e. arrays). Instead we have this situation where some functions take (non-const) T and others take T*, so sometimes you have to call functions with foo and other times it's just foo, and the presence or absence of doesn't signify anything important (for example, it doesn't tell you whether the callee mutates foo or not) It seems to me that the majority of languages have recognized that references are easier to work with than pointers, and not just because you can write foo.f() instead of foo-f(). That is, I don't think auto-deref (where foo.f() means (*foo).f()) is enough to eliminate the pain of working with pointers instead of references. Would it help to define ~T, T and @T as reference types rather than pointer types? I'm sure there are some obvious objections, but perhaps the problems could be ironed out by someone with a better grasp of Rust. I'd also like to comment on the removal of move. While I recognize explicit moves everywhere might be inconvenient, I have noticed that newbies on the list (including myself) are often confused by them. Would a compromise be in order, in which moves are implicit except when passing parameters to methods? I like Bill Byers' suggestion for call-site parameters of the form move x, mut x and just x for implicit copy / borrow. Patrick mentioned the annoyance of writing let move x = move foo(move y); but let x = foo(move y); doesn't sound so onerous, and it says something important about foo() that someone reading the code might not realize. Everyone's had their fair share of issues with autoref and autoderef, and it's worth considering removing certain portions of it from the compiler. The discussion around this has been rooted in the past, but has recently been brought up as part of https://github.com/mozilla/rust/issues/10504. The current proposal is to remove all autoref except for function invocations and indexing operations. The method of creating T from ~T would be `let foo: T = foo` or `*foo`. Vectors and strings can't currently benefit from the `*foo` syntax, but they will hopefully be able to do such once DST lands. In the meantime coercion via type ascription will work and they also have `as_slice` methods. There are a few reasons backing this proposal: 1. It's inconsistent to have magical autoref in some places, but not in other places. 2. The camp of less compiler magic is better can fly their flag over this change. 3. Code readability does not necessarily benefit from autoref on arguments: let a = ~Foo; foo(a); // reading this code looks like it moves `a` fn foo(_: Foo) {} // ah, nevermind, it doesn't move `a`! let mut a = ~[ ... ]; sort(a); // not only does this not move `a`, but it mutates it! ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
If the concern is mutability rather than details of the indirection used, then one option would be: let mut a = ~[y]; push(mut a, b); It would be a bit odd to do: let mut x = 0; incr(mut x); instead of: incr(mut x); as long as you're keeping a strict pointer vs. value mental model, but part of the motivation of considering this in the first place is a weakening of this model, so I'm not sure how it ends up on balance. Sort of starts to seem like passing modes on the caller side, though still implemented using explicit memory layout on the callee side and autoref/autoborrow acting as the bridge. On Wednesday, November 20, 2013, Patrick Walton wrote: On 11/20/13 5:05 AM, Sanghyeon Seo wrote: I am against this proposal. As I understand, we will still keep autoref, autoborrow, etc. in method calls. So I don't think inconsistency argument holds. Neither does local readability argument. It is a good point that a true emphasis on consistency between methods and functions would have us perform auto-deref and auto-ref on all argument positions. Then the LHS of `.` and arguments would truly be treated the same way, which dovetails with UFCS pretty nicely. I'm pretty nervous about performing auto-ref on function arguments, though, *especially* `mut` auto-ref. For some reason I expect that this: let mut a = ~[x]; a.push(hi); may mutate `a`, while: let mut a = ~[y]; push(a, b); will *not* mutate `a`. Do others have the same expectation? Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 11/20/13 10:23 AM, David Piepgrass wrote: I'm wondering something. Have the Rust developers considered the possibility of using references instead of pointers? It seems to me that this would eliminate a lot of the need for autoderef. Now I'm not well-equipped to talk about Rust (some of the rules I am totally ignorant about, e.g. I know rust has a ref keyword but the tutorial does not mention it) so let me talk about C++ instead. C++, of course, has references, which are exactly like pointers except they are always implicitly dereferenced: int y = 123; int* x1 = y; // *x1 == y x1 == y int x2 = y; // x2 == y x2 == y In fact, with one small tweak, C++ could have entirely eliminated the need for pointers (except array pointers), and references could have been used for everything except arrays. The reason references cannot replace pointers is that the pointer--the true identity of a reference--is immutable. But that's easily fixable though, by defining x2 as an lvalue: x1 = z; x2 = z; // Aha! We've changed the pointer inside the reference If the second line were legal, then references could do everything that pointers could do; in that case I think that, for consistency, people would have eventually standardized on using references for everything that doesn't require pointer arithmetic (i.e. arrays). Instead we have this situation where some functions take (non-const) T and others take T*, so sometimes you have to call functions with foo and other times it's just foo, and the presence or absence of doesn't signify anything important (for example, it doesn't tell you whether the callee mutates foo or not) This is orthogonal, and I don't want to make that change. I like the explicitness of pointers. It seems to me that the majority of languages have recognized that references are easier to work with than pointers, and not just because you can write foo.f() instead of foo-f(). That is, I don't think auto-deref (where foo.f() means (*foo).f()) is enough to eliminate the pain of working with pointers instead of references. This is because in most languages all object types are reference types and they rely on a GC, following Lisp. Would it help to define ~T, T and @T as reference types rather than pointer types? I'm sure there are some obvious objections, but perhaps the problems could be ironed out by someone with a better grasp of Rust. Doesn't work with custom smart pointers. I'd also like to comment on the removal of move. While I recognize explicit moves everywhere might be inconvenient, I have noticed that newbies on the list (including myself) are often confused by them. Would a compromise be in order, in which moves are implicit except when passing parameters to methods? I like Bill Byers' suggestion for call-site parameters of the form move x, mut x and just x for implicit copy / borrow. Patrick mentioned the annoyance of writing let move x = move foo(move y); but let x = foo(move y); doesn't sound so onerous, and it says something important about foo() that someone reading the code might not realize. -1. It'd be inconsistent to require it only in parameter position. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On Nov 20, 2013, at 8:52 AM, Corey Richardson co...@octayn.net wrote: I do, and I agree a lot with Niko's arguments from the meeting. I agree that * is a bit ugly, but I prefer the ugly syntax to the ambiguity. I don't find Kevin's argument particularly convincing; those changes can make your code fail to compile, but *not* to transparently behave differently (simply by changing the types of the args). Sure they can. fn foo(_: int) {} let mut x = 0; foo(x); x -= 1; println!({}, x); prints -1. Change the first line to fn foo(_: uint) {} and now this prints 18446744073709551615 (on my 64-bit machine). -Kevin___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On Wed, Nov 20, 2013 at 10:27 AM, Patrick Walton pcwal...@mozilla.comwrote: On 11/20/13 10:23 AM, David Piepgrass wrote: snip It seems to me that the majority of languages have recognized that references are easier to work with than pointers, and not just because you can write foo.f() instead of foo-f(). That is, I don't think auto-deref (where foo.f() means (*foo).f()) is enough to eliminate the pain of working with pointers instead of references. This is because in most languages all object types are reference types and they rely on a GC, following Lisp. But const references work out pretty well in C++, and it'd be hard to accuse C++ of relying on GC. Would it help to define ~T, T and @T as reference types rather than pointer types? I'm sure there are some obvious objections, but perhaps the problems could be ironed out by someone with a better grasp of Rust. Doesn't work with custom smart pointers. I'm sure something can be worked out for smart pointers with the help of some kind of AutoDeref trait. I'd also like to comment on the removal of move. While I recognize explicit moves everywhere might be inconvenient, I have noticed that newbies on the list (including myself) are often confused by them. Would a compromise be in order, in which moves are implicit except when passing parameters to methods? I like Bill Byers' suggestion for call-site parameters of the form move x, mut x and just x for implicit copy / borrow. Patrick mentioned the annoyance of writing let move x = move foo(move y); but let x = foo(move y); doesn't sound so onerous, and it says something important about foo() that someone reading the code might not realize. -1. It'd be inconsistent to require it only in parameter position. Well, it *would* be kind of consistent with Rust's policy type inference everywhere, except in function parameters. Also, - look at this thread - people are concerned about ambiguous mutability and moved-ness properties of function parameters, not about ambiguity of the level of indirection. I think that reference semantics for pointers combined with foo(mut x); foo(move y)'; would address the former while not introducing more syntax noise required to disambiguate the latter. Vadim ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On Wed, Nov 20, 2013 at 11:23:57AM -0700, David Piepgrass wrote: I'm wondering something. Have the Rust developers considered the possibility of using references instead of pointers? It seems to me that this would eliminate a lot of the need for autoderef. Now I'm not well-equipped to talk about Rust (some of the rules I am totally ignorant about, e.g. I know rust has a ref keyword but the tutorial does not mention it) so let me talk about C++ instead. I've thought about it, yes. When first designing the system, we were aiming for C-like semantics, which expose the underlying machinery of the computer more clearly. But I've since come to appreciate C++ references as well, though they are the poster child for creating invisible links between caller and callee. It's not entirely clear to me how C++ like references combine with type inference, particularly the H-M-like variant we are using now. In C++, there is no *operator* to create (or dereference) a reference, instead it is driven implicitly by type coercion (the very topic under discussion, in fact). Since C++ has such limited inference, this works out reasonably OK, but in Rust I imagine it'd be more troublesome. For example: let arr = ~[1, 2, 3]; let v = ~[]; v.push(arr[0]); v.push(arr[1]); v.push(arr[2]); I could legally infer the type of `v` to be either `~[int]` or `~[int]` here, the meaning is very different, and there is no real way for the user to be explicit about the difference other than supplying some type annotations. We're not immune to this. Our own coercions also potentially interact with inference. They are relatively limited, though, and we tend to have explicit operators as well, which helps. [1] Niko [1] See the interactions with inference section in my post. :) ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 2013-11-20, at 18:27, Patrick Walton pcwal...@mozilla.com wrote: On 11/19/13 9:42 PM, Tommi wrote: Our problem is that, given let arg: ~A;, seeing only foo(arg) in code doesn't tell us whether arg is moved or borrowed. The proposed solution is that auto-borrowing in that context would be deprecated and thus would require an explicit borrowing: foo(*arg). Now, given that it seems that the upcoming UFCS would simply re-write arg.foo() to foo(arg), it would mean that seeing only arg.foo() in code doesn't tell us whether arg is moved or borrowed. Thus, the proposed solution would fix only half of the problem. Again, I don't see this as an argument against the proposal. The dot operator is always magical. Magic on one place doesn't justify magic everywhere. Patrick The goal of the proposal is to disambiguate code by forcing programmers to write foo(*arg) instead of foo(arg). But I claim that the proposal will at least partly fail to achieve that goal because programmers will simply resort to writing arg.foo() rather than foo(*arg), and we finish where we started.___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
While I'm trying to argue why the proposed solution is not a full solution to the proposed problem, I don't even think that the proposed problem is a problem. Here's why: if you make a call foo(arg) and never use arg after that, then you don't care if arg gets moved or borrowed. And if you try to use arg afterwards and foo did in fact move it previously, then your IDE is going to tell you about it by drawing a red squiggly line under that incorrect use of arg.___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
This strikes me as a good point. I think the incongruity this would create between functions and methods -- along with the additional syntactic burden -- could lead to the use of functions feeling second class in some sense. Also, how would static methods be treated? Cheers, Tim On Wed, Nov 20, 2013 at 8:47 PM, Tommi rusty.ga...@icloud.com wrote: On 2013-11-20, at 18:27, Patrick Walton pcwal...@mozilla.com wrote: On 11/19/13 9:42 PM, Tommi wrote: Our problem is that, given let arg: ~A;, seeing only foo(arg) in code doesn't tell us whether arg is moved or borrowed. The proposed solution is that auto-borrowing in that context would be deprecated and thus would require an explicit borrowing: foo(*arg). Now, given that it seems that the upcoming UFCS would simply re-write arg.foo() to foo(arg), it would mean that seeing only arg.foo() in code doesn't tell us whether arg is moved or borrowed. Thus, the proposed solution would fix only half of the problem. Again, I don't see this as an argument against the proposal. The dot operator is always magical. Magic on one place doesn't justify magic everywhere. Patrick The goal of the proposal is to disambiguate code by forcing programmers to write foo(*arg) instead of foo(arg). But I claim that the proposal will at least partly fail to achieve that goal because programmers will simply resort to writing arg.foo() rather than foo(*arg), and we finish where we started. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
What about @/Gc types then? You could still potentially reuse them. -- Ziad On Wed, Nov 20, 2013 at 5:58 PM, Tommi rusty.ga...@icloud.com wrote: While I'm trying to argue why the proposed solution is not a full solution to the proposed problem, I don't even think that the proposed problem is a problem. Here's why: if you make a call foo(arg) and never use arg after that, then you don't care if arg gets moved or borrowed. And if you try to use arg afterwards and foo did in fact move it previously, then your IDE is going to tell you about it by drawing a red squiggly line under that incorrect use of arg. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On Wed, Nov 20, 2013 at 5:58 PM, Tommi rusty.ga...@icloud.com wrote: Here's why: if you make a call foo(arg) and never use arg after that, then you don't care if arg gets moved or borrowed. And if you try to use arg afterwards and foo did in fact move it previously, then your IDE is going to tell you about it by drawing a red squiggly line under that incorrect use of arg. This is from the point of view of the person writing the code. What about someone reading the code? He/she would either have to go through the remainder of the function/scope to make sure that `arg` was not being used (if it were, then it was a borrow, otherwise, it could have been either a borrow or a move); or they would have to look at the signature of `foo()`; as opposed to just being able to tell right there at the call site. -- Ziad ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On Nov 20, 2013, at 6:21 PM, Ziad Hatahet hata...@gmail.com wrote: On Wed, Nov 20, 2013 at 5:58 PM, Tommi rusty.ga...@icloud.com wrote: Here's why: if you make a call foo(arg) and never use arg after that, then you don't care if arg gets moved or borrowed. And if you try to use arg afterwards and foodid in fact move it previously, then your IDE is going to tell you about it by drawing a red squiggly line under that incorrect use of arg. This is from the point of view of the person writing the code. What about someone reading the code? He/she would either have to go through the remainder of the function/scope to make sure that `arg` was not being used (if it were, then it was a borrow, otherwise, it could have been either a borrow or a move); or they would have to look at the signature of `foo()`; as opposed to just being able to tell right there at the call site. Why does the reader particularly care? Presumably the code compiles (otherwise I have to ask why they're reading broken code, which is broken in a way that the compiler would trivially explain), so either it's moved and the arg isn't used again, or it's borrowed. It only really matters if the reader wants to subsequently modify the code, at which point they're now the person writing the code. And we get back to Tommi's point that the compiler (or, ideally, an IDE) will tell you if you try and use a moved value. -Kevin___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 2013-11-21, at 4:16, Ziad Hatahet hata...@gmail.com wrote: What about @/Gc types then? You could still potentially reuse them. -- Ziad On Wed, Nov 20, 2013 at 5:58 PM, Tommi rusty.ga...@icloud.com wrote: While I'm trying to argue why the proposed solution is not a full solution to the proposed problem, I don't even think that the proposed problem is a problem. Here's why: if you make a call foo(arg) and never use arg after that, then you don't care if arg gets moved or borrowed. And if you try to use arg afterwards and foo did in fact move it previously, then your IDE is going to tell you about it by drawing a red squiggly line under that incorrect use of arg. Garbage collected variables are owned by the runtime and the programmer cannot move (change the ownership of) them. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
Additionally, we discussed this today at our weekly meeting, and the minutes can be found here: https://github.com/mozilla/rust/wiki/Meeting-weekly-2013-11-19#autoderef ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
In general, I’m not a fan of *, and I like auto-borrowing (autoref sounds like it turns T into T, not ~T into T). I understand the arguments to get rid of it though. BTW, you said that the current proposal still includes autoref for function invocation. That sounds to me like autoref would still apply to x in foo(x). Is there something else you mean by that? As I see it, the two interesting cases to consider for removing it are: 1. let x = ~Foo; foo(x); // looks like it moves x when it just borrows it 2. let mut a = ~[ … ]; sort(a); // mutates a To me, the second case seems pretty clear that it shouldn’t auto-borrow a ~T to a mut T, and instead require sort(mut *a). It’s pretty ugly, but it makes it clear what’s going on. This case isn’t particularly common, which is why losing autoref isn’t very tragic. The first case is the most common case, and it seems a shame to require *x everywhere. I understand the argument that it looks like x is moved until you read the function signature of foo(), but I’m not convinced that’s important enough to uglify the code. Arguably, you already have to know what foo() does to understand what the code is doing. The basic argument, as I see it, is that the type signature of foo() shouldn’t change how the calling code treats its argument. If foo() changed from `fn foo(_: T)` to `fn foo(_: ~T)` then `x` would start being moved instead of borrowed, which could cause errors in subsequent code in the caller. But the problem with this argument is that the type signature of foo() already changes how the calling code treats its argument, in a fashion that will surface errors in later code rather than at the call site. Notably, if the argument is not fully constrained to a specific type by the time foo(x) is called, it will further constrain the type that may not cause a problem until later. For example: fn foo(_: uint); fn bar(_: uint); let x = 1; foo(x); bar(x); This compiles just fine, but if foo() changes its argument to `int`, then the call to bar() will fail with a type error. More complex situations can be created using generic functions as well. In the case of autoref, the question is whether the value will be moved, rather than the type of the value, which is a slightly different issue but I’m not convinced it’s different enough that it needs to be specially addressed. The other problem with this is that, even with autoref removed, you can still hit the same problem of a seemingly-benign change elsewhere causing a compiler error in your code. Namely, if a struct has no destructor, and you rely on the ability to implicitly copy it (e.g. calling bar(x) where foo is fn bar(_: T)), then adding a destructor to the struct will cause your code to fail to compile, in just the same way that foo changing from `fn foo(_: T)` to `fn foo(_: ~T)` will today. Overall, there just doesn’t seem to me to be a compelling reason to remove the ability to auto-borrow ~T into T. Removing the ability to auto-borrow ~T into mut T is more sensible though. FWIW, a lot of why I like auto-borrowing is actually due to auto-slicing of ~[T] into [T] and ~str into str. But I assume that when (if) DST happens, auto-slicing will just be a case of auto-borrowing. And until that happens I’d really like to avoiding having to say foo(v.as_slice()). -Kevin On Nov 19, 2013, at 2:08 PM, Alex Crichton a...@crichton.co wrote: Hello rust-dev! Everyone's had their fair share of issues with autoref and autoderef, and it's worth considering removing certain portions of it from the compiler. The discussion around this has been rooted in the past, but has recently been brought up as part of https://github.com/mozilla/rust/issues/10504. The current proposal is to remove all autoref except for function invocations and indexing operations. The method of creating T from ~T would be `let foo: T = foo` or `*foo`. Vectors and strings can't currently benefit from the `*foo` syntax, but they will hopefully be able to do such once DST lands. In the meantime coercion via type ascription will work and they also have `as_slice` methods. There are a few reasons backing this proposal: 1. It's inconsistent to have magical autoref in some places, but not in other places. 2. The camp of less compiler magic is better can fly their flag over this change. 3. Code readability does not necessarily benefit from autoref on arguments: let a = ~Foo; foo(a); // reading this code looks like it moves `a` fn foo(_: Foo) {} // ah, nevermind, it doesn't move `a`! let mut a = ~[ ... ]; sort(a); // not only does this not move `a`, but it mutates it! The basic idea is that reasoning about code is no longer a local function decision, but rather you must understand all the pointer-ness of the called signatures to understand when a move happens or not. With no autoref, the code would look like let a = ~Foo; foo(*a); // clearly not passing by value let mut
Re: [rust-dev] Removing some autoref magic
Personally I would prefer if in Rust worked similar to const T in c++ In that case, you would not be able to tell whether a function argument was passed by value or by reference. I actually like this feature about Rust (C# has it too with the `ref` keyword). -- Ziad On Tue, Nov 19, 2013 at 4:54 PM, Vadim vadi...@gmail.com wrote: So why did Rust adopt auto-moving instead of explicit moving? If the second example had to be written as foo(move a) there would be no confusion. The and the third example should arguably be sort(mut a.as_mut_slice()). Personally I would prefer if in Rust worked similar to const T in c++ (i.e. for most intents and purposes you can treat a reference as a value), otherwise half of the arguments on each function call will have to be adorned with ampersands. Can we have it such that foo(a) would be guaranteed to not mutate or move a and require mut or move prefix otherwise? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
If the function is not going to mutate it, I don't really care if it's by-ref or by-value. That's why I said const T, not T. Well, except for the case when a function caches a reference to something which I later mutate. But borrow checking will prevent that. I would also be totally fine with letting the compiler figure out how immutable arguments are passed (based on data type size, existence of destructors, and whatnot). This might not be the best idea for binary compatibility of public APIs, but I don't see why it can't be done with crate-private functions. On Tue, Nov 19, 2013 at 5:07 PM, Ziad Hatahet hata...@gmail.com wrote: Personally I would prefer if in Rust worked similar to const T in c++ In that case, you would not be able to tell whether a function argument was passed by value or by reference. I actually like this feature about Rust (C# has it too with the `ref` keyword). -- Ziad On Tue, Nov 19, 2013 at 4:54 PM, Vadim vadi...@gmail.com wrote: So why did Rust adopt auto-moving instead of explicit moving? If the second example had to be written as foo(move a) there would be no confusion. The and the third example should arguably be sort(mut a.as_mut_slice()). Personally I would prefer if in Rust worked similar to const T in c++ (i.e. for most intents and purposes you can treat a reference as a value), otherwise half of the arguments on each function call will have to be adorned with ampersands. Can we have it such that foo(a) would be guaranteed to not mutate or move a and require mut or move prefix otherwise? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
While i like auto magic eg you could let region analysis work out the pointer type , borrowed pointers etc ,however you need to be very careful . C# uses ref but autoboxing is a major issue , you dont know when the struct is boxed ( there are some places besides the obvious virt call ) and then you incur boxing costs defeating the whole point of value types . The vast majority of C# developers only use structs for a few corner cases eg a type holding a few values ( many dont use value types at all) , they could go further but often it will get boxed somewhere and kill the benefits so why go through the hassle. Ben On Wed, Nov 20, 2013 at 9:35 AM, Vadim vadi...@gmail.com wrote: If the function is not going to mutate it, I don't really care if it's by-ref or by-value. That's why I said const T, not T. Well, except for the case when a function caches a reference to something which I later mutate. But borrow checking will prevent that. I would also be totally fine with letting the compiler figure out how immutable arguments are passed (based on data type size, existence of destructors, and whatnot). This might not be the best idea for binary compatibility of public APIs, but I don't see why it can't be done with crate-private functions. On Tue, Nov 19, 2013 at 5:07 PM, Ziad Hatahet hata...@gmail.com wrote: Personally I would prefer if in Rust worked similar to const T in c++ In that case, you would not be able to tell whether a function argument was passed by value or by reference. I actually like this feature about Rust (C# has it too with the `ref` keyword). -- Ziad On Tue, Nov 19, 2013 at 4:54 PM, Vadim vadi...@gmail.com wrote: So why did Rust adopt auto-moving instead of explicit moving? If the second example had to be written as foo(move a) there would be no confusion. The and the third example should arguably be sort(mut a.as_mut_slice()). Personally I would prefer if in Rust worked similar to const T in c++ (i.e. for most intents and purposes you can treat a reference as a value), otherwise half of the arguments on each function call will have to be adorned with ampersands. Can we have it such that foo(a) would be guaranteed to not mutate or move a and require mut or move prefix otherwise? ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 2013-11-20, at 1:40, Benjamin Striegel ben.strie...@gmail.com wrote: If autoref still happens on methods, does this mean that you'd be able to get around the need to do: sort(mut *foo); ...by turning it into a method? foo.sort(); I like how your argument nudges this whole discussion a few inches to the left. Now we see that disallowing auto-borrowing from regular function arguments doesn't buy us anything unless we also disallow it from the target of method invocation syntax (dot notation). And that's obviously assuming the upcoming UFCS feature. I doubt that anyone would like to be writing (*foo).call_method() all day long. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 11/19/13 7:43 PM, Tommi wrote: I like how your argument nudges this whole discussion a few inches to the left. Now we see that disallowing auto-borrowing from regular function arguments doesn't buy us anything unless we also disallow it from the target of method invocation syntax (dot notation). And that's obviously assuming the upcoming UFCS feature. I doubt that anyone would like to be writing (*foo).call_method() all day long. Magic in one part of the language isn't equivalent to magic everywhere in the language. The dot operator is already magical in several ways: it autoderefs for fields and searches through all traits in scope. This proposal would confine the magic to that operator and that operator alone, preventing it from leaking out into other parts of the language. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 2013-11-20, at 6:24, Patrick Walton pcwal...@mozilla.com wrote: On 11/19/13 7:43 PM, Tommi wrote: I like how your argument nudges this whole discussion a few inches to the left. Now we see that disallowing auto-borrowing from regular function arguments doesn't buy us anything unless we also disallow it from the target of method invocation syntax (dot notation). And that's obviously assuming the upcoming UFCS feature. I doubt that anyone would like to be writing (*foo).call_method() all day long. Magic in one part of the language isn't equivalent to magic everywhere in the language. The dot operator is already magical in several ways: it autoderefs for fields and searches through all traits in scope. This proposal would confine the magic to that operator and that operator alone, preventing it from leaking out into other parts of the language. Patrick Yeah, my claim that it doesn't buy us anything stemmed from a misconception that arg.foo() could move arg, given let arg: ~A; and fn foo(x: ~A) {...}. I had already forgotten that Rust UFCS was specified differently from D's. To anyone not following, in D it would be a simple re-write to foo(arg) and, in Rust, to foo(arg).___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Removing some autoref magic
On 2013-11-20, at 7:13, Tommi rusty.ga...@icloud.com wrote: Yeah, my claim that it doesn't buy us anything stemmed from a misconception that arg.foo() could move arg, given let arg: ~A; and fn foo(x: ~A) {...}. I had already forgotten that Rust UFCS was specified differently from D's. To anyone not following, in D it would be a simple re-write to foo(arg) and, in Rust, to foo(arg). Scratch that. I seem to have different things mixed up now. I found this ticket https://github.com/mozilla/rust/issues/6974 again and it seems I should stand by my initial argument. Let me explain my argument again: Our problem is that, given let arg: ~A;, seeing only foo(arg) in code doesn't tell us whether arg is moved or borrowed. The proposed solution is that auto-borrowing in that context would be deprecated and thus would require an explicit borrowing: foo(*arg). Now, given that it seems that the upcoming UFCS would simply re-write arg.foo() to foo(arg), it would mean that seeing only arg.foo() in code doesn't tell us whether arg is moved or borrowed. Thus, the proposed solution would fix only half of the problem.___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev