Re: [rust-dev] Mutable files
Patrick Walton pcwal...@mozilla.com wrote: On 7/21/14 2:22 PM, Tobias Müller wrote: We discussed this with Bartosz literally for weeks (him being a fan of auto_ptr for too long, later completely converted against it and I take credit for that :o)). With auto_ptr this was possible: auto_ptrint a(new int); auto_ptrint b = a; It would nullify a with copy syntax. That code won't compile with unique_ptr; you'd need an explicit move(a). It only got worse from there: passing into functions, member variables... MOVING WITH COPY SYNTAX DOES NOT WORK. It's cut and dried. Please don't snip the attribution, that was a quote! ... in C++. Not in Rust. That's because, unlike C++, Rust is designed from the ground up to support moves and copies in a first class way. It's just strange that you can change the semantic of an already existing operation just by adding new capabilities. Adding traits should define new operations with new semantics, not changing the semantics of existing operations. At least that's how it works for all other traits, and deviating from that is at least surprising. Hence the Opt-In Built-In Traits proposal Opt-In built-In traits makes things a bit better but my point is still valid. By adding Copy (implicitly or explicitly) you remove the possibility of move semantics from the type. Usually you don't work alone on a project and some coworker adding Copy to a type that I expected to be Move may be fatal. No other trait removed works like that. Maybe the syntax was just too heavy? Any syntax at all is too much. I am convinced of that. I'm still not convinced but maybe my fear is unjustified. Time will tell. Tobi ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
On Mon, Jul 21, 2014 at 2:45 PM, Patrick Walton pcwal...@mozilla.com wrote: ... in C++. Not in Rust. That's because, unlike C++, Rust is designed from the ground up to support moves and copies in a first class way. As a C++ dev, I feel the need to say THANK YOU for that. Rust being designed with first-class move support is a major feature for me; it's something I highlight when I talk about Rust with other C++ devs and it's universally applauded. It's just strange that you can change the semantic of an already existing operation just by adding new capabilities. Adding traits should define new operations with new semantics, not changing the semantics of existing operations. At least that's how it works for all other traits, and deviating from that is at least surprising. Hence the Opt-In Built-In Traits proposal Maybe the syntax was just too heavy? Any syntax at all is too much. I am convinced of that. 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] Mutable files
On 23/07/14 07:10, Tobias Müller wrote: ... in C++. Not in Rust. That's because, unlike C++, Rust is designed from the ground up to support moves and copies in a first class way. It's just strange that you can change the semantic of an already existing operation just by adding new capabilities. Adding traits should define new operations with new semantics, not changing the semantics of existing operations. At least that's how it works for all other traits, and deviating from that is at least surprising. Hence the Opt-In Built-In Traits proposal Opt-In built-In traits makes things a bit better but my point is still valid. By adding Copy (implicitly or explicitly) you remove the possibility of move semantics from the type. Usually you don't work alone on a project and some coworker adding Copy to a type that I expected to be Move may be fatal. No other trait removed works like that. You can't just add Copy to anything: the contents has to be Copy itself, and, you can't have a destructor on your type (i.e. a Drop implementation removes the possibility to be Copy). Thus, almost all types for which by-value uses *should* invalidate the source (i.e. move semantics) are automatically not Copy anyway. The only way one can get a fatal error due to an incorrect Copy implementation is if the type with the impl is using `unsafe` code internally. In this case, that whole API needs to be considered very carefully anyway, ensuring correctness by avoiding Copy is just part of it. I'll also note that an implementation of Copy just states the a byte-copy of a value is also a semantic copy, it doesn't offer any control over how the copy is performed. At runtime, by-value use of a Copy type is essentially identical to a by-value use of a non-Copy type (both are memcpy's of the bytes), the only major difference is the compiler statically prevents further uses of the source for non-Copy ones. Huon ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
On 2014-07-21 06:06, Patrick Walton wrote: On 7/20/14 9:04 PM, Patrick Walton wrote: On 7/20/14 8:12 PM, David Henningsson wrote: Cool, thanks for the answer. These restrictions seem somewhat complex. They are required. Otherwise we would end up with a C++-like situation where copies end up happening too frequently. Also note that these rules, far from being complex, end up making the language much simpler than C++, as copy (or D-like postblit) constructors are not required. All Rust types, if they are copyable at all, can be copied by simply moving bits around. Fair enough. I just guess it takes a while getting used to, that you sometimes can't use a variable after you've sent it as a parameter to a function. Also now having read the RFC for Opt-in builtin traits which you mentioned earlier, I think this RFC makes a lot of sense. Especially the API Stability and Pedagogy points would have been helpful here. // David ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
On 7/21/14 8:49 AM, Tobias Müller wrote: Patrick Walton pcwal...@mozilla.com wrote: On 7/20/14 8:12 PM, David Henningsson wrote: From a language design perspective, maybe it would be more intuitive to have different syntaxes for copy and move, like: As a rust newbie, that aspect aways makes me a bit nervous. Two quite different operations with the same syntax and and simply changing a detail in the struct can be enough to switch between the two. This is the reason for Opt-In Built-In Traits. AFAIK this also was one of the reasons (if not _the_ reason) why std::auto_ptr was deprecated in C++. No, `auto_ptr` was deprecated because it copies, not moves, making it hard to sensibly use in containers (among other things). Comparisons between C++ aren't really relevant anyway because the compiler catches any use-after-move at *compile time*, rather than at runtime. This means that mistaking the two doesn't cause any harm: * Causing a move when you thought you were copying results in a compiler error. * Causing a copy when you thought you were moving is harmless, as any implicit copy in Rust has *exactly the same runtime semantics* as a move, except that the compiler prevents you from using the value again. Again, we had that world before. It was extremely annoying to write move all over the place. Be careful what you wish for. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
Patrick Walton pcwal...@mozilla.com wrote: On 7/21/14 8:49 AM, Tobias Müller wrote: As a rust newbie, that aspect aways makes me a bit nervous. Two quite different operations with the same syntax and and simply changing a detail in the struct can be enough to switch between the two. This is the reason for Opt-In Built-In Traits. AFAIK this also was one of the reasons (if not _the_ reason) why std::auto_ptr was deprecated in C++. No, `auto_ptr` was deprecated because it copies, not moves, making it hard to sensibly use in containers (among other things). Quoting Andrei Alexandrescu on digitalmars.d: We discussed this with Bartosz literally for weeks (him being a fan of auto_ptr for too long, later completely converted against it and I take credit for that :o)). With auto_ptr this was possible: auto_ptrint a(new int); auto_ptrint b = a; It would nullify a with copy syntax. That code won't compile with unique_ptr; you'd need an explicit move(a). It only got worse from there: passing into functions, member variables... MOVING WITH COPY SYNTAX DOES NOT WORK. It's cut and dried. Andrei - But you are right, Rust is not C++, it's actually the other way round that makes me nervous. Comparisons between C++ aren't really relevant anyway because the compiler catches any use-after-move at *compile time*, rather than at runtime. This means that mistaking the two doesn't cause any harm: * Causing a move when you thought you were copying results in a compiler error. * Causing a copy when you thought you were moving is harmless, as any implicit copy in Rust has *exactly the same runtime semantics* as a move, except that the compiler prevents you from using the value again. From a performance point of view that may be true, but you may lose desired semantics. If you want an instance of a type to be move-only, but later decide that copying that type is still useful in another place, then you lose the guarantee in the first place. It's just strange that you can change the semantic of an already existing operation just by adding new capabilities. Adding traits should define new operations with new semantics, not changing the semantics of existing operations. At least that's how it works for all other traits, and deviating from that is at least surprising. Again, we had that world before. It was extremely annoying to write move all over the place. Be careful what you wish for. Maybe the syntax was just too heavy? Tobi ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
On 7/21/14 2:22 PM, Tobias Müller wrote: We discussed this with Bartosz literally for weeks (him being a fan of auto_ptr for too long, later completely converted against it and I take credit for that :o)). With auto_ptr this was possible: auto_ptrint a(new int); auto_ptrint b = a; It would nullify a with copy syntax. That code won't compile with unique_ptr; you'd need an explicit move(a). It only got worse from there: passing into functions, member variables... MOVING WITH COPY SYNTAX DOES NOT WORK. It's cut and dried. ... in C++. Not in Rust. That's because, unlike C++, Rust is designed from the ground up to support moves and copies in a first class way. It's just strange that you can change the semantic of an already existing operation just by adding new capabilities. Adding traits should define new operations with new semantics, not changing the semantics of existing operations. At least that's how it works for all other traits, and deviating from that is at least surprising. Hence the Opt-In Built-In Traits proposal Maybe the syntax was just too heavy? Any syntax at all is too much. I am convinced of that. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Mutable files
Hi, Consider these two examples: 1) let mut file = File::open(filename); file.read(buf); 2) let file = File::open(filename); let mut reader = BufferedReader::new(file); reader.read(buf); My question is: in example 2, why doesn't BufferedReader need file to be mutable? After all, BufferedReader ends up calling file.read(), which needs a mutable reference to the file. It looks like I'm able to bypass the mutability requirement, just because I wrap the file inside a BufferedReader? // David ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
That's right. `BufferedReader` takes the `Reader` it wraps by-value, but the `read` method takes `mut self`. Moving something doesn't require it to be stored in a mutable variable, but taking a `mut` to it does. On Sun, Jul 20, 2014 at 6:29 PM, David Henningsson di...@ubuntu.com wrote: Hi, Consider these two examples: 1) let mut file = File::open(filename); file.read(buf); 2) let file = File::open(filename); let mut reader = BufferedReader::new(file); reader.read(buf); My question is: in example 2, why doesn't BufferedReader need file to be mutable? After all, BufferedReader ends up calling file.read(), which needs a mutable reference to the file. It looks like I'm able to bypass the mutability requirement, just because I wrap the file inside a BufferedReader? // David ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev -- http://octayn.net/ ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
On 7/20/14 6:29 PM, David Henningsson wrote: Hi, Consider these two examples: 1) let mut file = File::open(filename); file.read(buf); 2) let file = File::open(filename); let mut reader = BufferedReader::new(file); reader.read(buf); My question is: in example 2, why doesn't BufferedReader need file to be mutable? After all, BufferedReader ends up calling file.read(), which needs a mutable reference to the file. It looks like I'm able to bypass the mutability requirement, just because I wrap the file inside a BufferedReader? Because `BufferedReader::new` moves `file` and takes ownership of it. (You can see this if you try to use `file` again: the compiler will prevent you.) Mutability is inherited through ownership in Rust: that is, the current owner determines the mutability of a piece of data. So, the mutability of `reader` determines the mutability of the `File` object at the time you try to read, and the mutability restriction is satisfied. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
On 2014-07-21 03:33, Patrick Walton wrote: On 7/20/14 6:29 PM, David Henningsson wrote: Hi, Consider these two examples: 1) let mut file = File::open(filename); file.read(buf); 2) let file = File::open(filename); let mut reader = BufferedReader::new(file); reader.read(buf); My question is: in example 2, why doesn't BufferedReader need file to be mutable? After all, BufferedReader ends up calling file.read(), which needs a mutable reference to the file. It looks like I'm able to bypass the mutability requirement, just because I wrap the file inside a BufferedReader? Because `BufferedReader::new` moves `file` and takes ownership of it. (You can see this if you try to use `file` again: the compiler will prevent you.) Mutability is inherited through ownership in Rust: that is, the current owner determines the mutability of a piece of data. So, the mutability of `reader` determines the mutability of the `File` object at the time you try to read, and the mutability restriction is satisfied. Thanks for the quick answer! I did two more examples to try to understand when things are moved: 3) struct Dummy { foo: int, bar: int } let f = Dummy {foo: 10, bar: 5}; let mut g = f; // Here the assignment copies..? println!({}, f.foo + g.foo); // Ok 4) let f = File::open(filename); let mut g = f; // Here the assignment moves..? f.tell(); // Fails - use of moved value How come that the assignment moves in example 4), and copies in example 3)? // David ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
Because Foo is a POD type (implements the Copy trait). Essentially, types that can be copied by copying bits only (not allocating) are POD types, and all others move. This may be changed with the Opt-In Built-in Traits proposal so that POD types must be specially declared to implement Copy before they will copy. Patrick On July 20, 2014 7:39:35 PM PDT, David Henningsson di...@ubuntu.com wrote: On 2014-07-21 03:33, Patrick Walton wrote: On 7/20/14 6:29 PM, David Henningsson wrote: Hi, Consider these two examples: 1) let mut file = File::open(filename); file.read(buf); 2) let file = File::open(filename); let mut reader = BufferedReader::new(file); reader.read(buf); My question is: in example 2, why doesn't BufferedReader need file to be mutable? After all, BufferedReader ends up calling file.read(), which needs a mutable reference to the file. It looks like I'm able to bypass the mutability requirement, just because I wrap the file inside a BufferedReader? Because `BufferedReader::new` moves `file` and takes ownership of it. (You can see this if you try to use `file` again: the compiler will prevent you.) Mutability is inherited through ownership in Rust: that is, the current owner determines the mutability of a piece of data. So, the mutability of `reader` determines the mutability of the `File` object at the time you try to read, and the mutability restriction is satisfied. Thanks for the quick answer! I did two more examples to try to understand when things are moved: 3) struct Dummy { foo: int, bar: int } let f = Dummy {foo: 10, bar: 5}; let mut g = f; // Here the assignment copies..? println!({}, f.foo + g.foo); // Ok 4) let f = File::open(filename); let mut g = f; // Here the assignment moves..? f.tell(); // Fails - use of moved value How come that the assignment moves in example 4), and copies in example 3)? // David -- Sent from my Android phone with K-9 Mail. Please excuse my brevity.___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
Some types are implicitly copyable. They implement the built-in trait Copy. A type is Copy if it is a) numeric primitive (e.g. f32 or uint), or b) an immutable reference (e.g. Foo or str), or c) a raw pointer (e.g. *const Foo or *mut Foo), or d) a collection of Copy types (e.g. struct Foo { a: int, b: 'static str }). In addition, if a type implements Drop, it is no longer Copy. Steven Fackler On Sun, Jul 20, 2014 at 7:39 PM, David Henningsson di...@ubuntu.com wrote: On 2014-07-21 03:33, Patrick Walton wrote: On 7/20/14 6:29 PM, David Henningsson wrote: Hi, Consider these two examples: 1) let mut file = File::open(filename); file.read(buf); 2) let file = File::open(filename); let mut reader = BufferedReader::new(file); reader.read(buf); My question is: in example 2, why doesn't BufferedReader need file to be mutable? After all, BufferedReader ends up calling file.read(), which needs a mutable reference to the file. It looks like I'm able to bypass the mutability requirement, just because I wrap the file inside a BufferedReader? Because `BufferedReader::new` moves `file` and takes ownership of it. (You can see this if you try to use `file` again: the compiler will prevent you.) Mutability is inherited through ownership in Rust: that is, the current owner determines the mutability of a piece of data. So, the mutability of `reader` determines the mutability of the `File` object at the time you try to read, and the mutability restriction is satisfied. Thanks for the quick answer! I did two more examples to try to understand when things are moved: 3) struct Dummy { foo: int, bar: int } let f = Dummy {foo: 10, bar: 5}; let mut g = f; // Here the assignment copies..? println!({}, f.foo + g.foo); // Ok 4) let f = File::open(filename); let mut g = f; // Here the assignment moves..? f.tell(); // Fails - use of moved value How come that the assignment moves in example 4), and copies in example 3)? // David ___ 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] Mutable files
On 2014-07-21 04:43, Steven Fackler wrote: Some types are implicitly copyable. They implement the built-in trait Copy. A type is Copy if it is a) numeric primitive (e.g. f32 or uint), or b) an immutable reference (e.g. Foo or str), or c) a raw pointer (e.g. *const Foo or *mut Foo), or d) a collection of Copy types (e.g. struct Foo { a: int, b: 'static str }). In addition, if a type implements Drop, it is no longer Copy. Steven Fackler Cool, thanks for the answer. These restrictions seem somewhat complex. This wasn't very intuitive for me, so just throwing this out (feel free to ignore if it has already been discussed :-) ) From a language design perspective, maybe it would be more intuitive to have different syntaxes for copy and move, like: let mut g = f; /* Copies from f to g, error if f is a non-Copy type */ let mut g - f; /* Moves from f to g, error if trying to use f afterwards */ Or in the File/BufferedReader example, this would be something like: let f = File::open(filename); let mut reader = BufferedReader::new(- f); /* Bye bye f! */ I'm also afraid that if a library struct decides to change between a copy and non-copy type, this would cause subtle errors in users of that library that expected the other type. But if the compiler is guaranteed to catch all such errors even with today's handling, maybe that is not too much to worry about. On Sun, Jul 20, 2014 at 7:39 PM, David Henningsson di...@ubuntu.com mailto:di...@ubuntu.com wrote: On 2014-07-21 03:33, Patrick Walton wrote: On 7/20/14 6:29 PM, David Henningsson wrote: Hi, Consider these two examples: 1) let mut file = File::open(filename); file.read(buf); 2) let file = File::open(filename); let mut reader = BufferedReader::new(file); reader.read(buf); My question is: in example 2, why doesn't BufferedReader need file to be mutable? After all, BufferedReader ends up calling file.read(), which needs a mutable reference to the file. It looks like I'm able to bypass the mutability requirement, just because I wrap the file inside a BufferedReader? Because `BufferedReader::new` moves `file` and takes ownership of it. (You can see this if you try to use `file` again: the compiler will prevent you.) Mutability is inherited through ownership in Rust: that is, the current owner determines the mutability of a piece of data. So, the mutability of `reader` determines the mutability of the `File` object at the time you try to read, and the mutability restriction is satisfied. Thanks for the quick answer! I did two more examples to try to understand when things are moved: 3) struct Dummy { foo: int, bar: int } let f = Dummy {foo: 10, bar: 5}; let mut g = f; // Here the assignment copies..? println!({}, f.foo + g.foo); // Ok 4) let f = File::open(filename); let mut g = f; // Here the assignment moves..? f.tell(); // Fails - use of moved value How come that the assignment moves in example 4), and copies in example 3)? // David _ Rust-dev mailing list Rust-dev@mozilla.org mailto:Rust-dev@mozilla.org https://mail.mozilla.org/__listinfo/rust-dev 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] Mutable files
On 7/20/14 8:12 PM, David Henningsson wrote: Cool, thanks for the answer. These restrictions seem somewhat complex. They are required. Otherwise we would end up with a C++-like situation where copies end up happening too frequently. This wasn't very intuitive for me, so just throwing this out (feel free to ignore if it has already been discussed :-) ) From a language design perspective, maybe it would be more intuitive to have different syntaxes for copy and move, like: There used to be a unary move operator. This was a huge pain. match move x { Some(move y) = foo(move z); } And so on. I don't want to go back to that world. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Mutable files
On 7/20/14 9:04 PM, Patrick Walton wrote: On 7/20/14 8:12 PM, David Henningsson wrote: Cool, thanks for the answer. These restrictions seem somewhat complex. They are required. Otherwise we would end up with a C++-like situation where copies end up happening too frequently. Also note that these rules, far from being complex, end up making the language much simpler than C++, as copy (or D-like postblit) constructors are not required. All Rust types, if they are copyable at all, can be copied by simply moving bits around. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev