Mutable files

classic Classic list List threaded Threaded
20 messages Options
Reply | Threaded
Open this post in threaded view
|

Mutable files

David Henningsson
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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Patrick Walton-2
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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Corey Richardson
In reply to this post by David Henningsson
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 <[hidden email]> 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
> [hidden email]
> https://mail.mozilla.org/listinfo/rust-dev



--
http://octayn.net/
_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

David Henningsson
In reply to this post by Patrick Walton-2


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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Steven Fackler-2
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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev


_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Patrick Walton
In reply to this post by David Henningsson
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 <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

David Henningsson
In reply to this post by Steven Fackler-2


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 <[hidden email]
> <mailto:[hidden email]>> 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
>     [hidden email] <mailto:[hidden email]>
>     https://mail.mozilla.org/__listinfo/rust-dev
>     <https://mail.mozilla.org/listinfo/rust-dev>
>
>
_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Patrick Walton-2
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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Patrick Walton-2
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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

David Henningsson


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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Tobias Müller
In reply to this post by Patrick Walton-2
Patrick Walton <[hidden email]> 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.

AFAIK this also was one of the reasons (if not _the_ reason) why
std::auto_ptr was deprecated in C++.

Tobi

_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Patrick Walton-2
On 7/21/14 8:49 AM, Tobias Müller wrote:
> Patrick Walton <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Tobias Müller
Patrick Walton <[hidden email]> 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_ptr<int> a(new int);
auto_ptr<int> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Patrick Walton-2
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_ptr<int> a(new int);
> auto_ptr<int> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Tobias Müller
Patrick Walton <[hidden email]> 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_ptr<int> a(new int);
>> auto_ptr<int> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Strahinja Markovic
In reply to this post by Patrick Walton-2
On Mon, Jul 21, 2014 at 2:45 PM, Patrick Walton <[hidden email]> 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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev


_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Mutable files

Huon Wilson
In reply to this post by Tobias Müller
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
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Opt-In Built-In Traits (was: Mutable files)

David Henningsson
In reply to this post by Patrick Walton-2


On 2014-07-21 19:17, Patrick Walton wrote:

> On 7/21/14 8:49 AM, Tobias Müller wrote:
>> Patrick Walton <[hidden email]> 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.
>
> * 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.

I find these arguments compelling, but if what we want to accomplish is
a conscious choice between copy and move every time somebody makes a new
struct, maybe "#[Deriving(Data)] struct Foo" vs "struct Foo" is not
first-class enough.

Maybe the move vs copy should be done by using different keywords, a few
brainstorming examples:

  * "datastruct" for copy, "struct" for move
  * "simplestruct" for copy, "complexstruct" for move
  * "struct" for copy, "class" or "object" for move

...etc.

// David
_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Opt-In Built-In Traits (was: Mutable files)

Kevin Ballard-2
On Wed, Jul 23, 2014, at 12:52 PM, David Henningsson wrote:

>
>
> On 2014-07-21 19:17, Patrick Walton wrote:
> > On 7/21/14 8:49 AM, Tobias Müller wrote:
> >> Patrick Walton <[hidden email]> 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.
> >
> > * 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.
>
> I find these arguments compelling, but if what we want to accomplish is
> a conscious choice between copy and move every time somebody makes a new
> struct, maybe "#[Deriving(Data)] struct Foo" vs "struct Foo" is not
> first-class enough.
>
> Maybe the move vs copy should be done by using different keywords, a few
> brainstorming examples:
>
>   * "datastruct" for copy, "struct" for move
>   * "simplestruct" for copy, "complexstruct" for move
>   * "struct" for copy, "class" or "object" for move

What would this solve? Nobody who’s using a type is going to care about
the keyword used to introduce the type, they’re only going to care about
the behavior of the type. Using `datastruct` instead of `struct` will
have zero impact on the people writing

let x: Foo = y;

Actually, the whole notion of having to intentionally describe on every
struct whether you want it to be Copy is my biggest objection to opt-in
traits. The API Stability / documentation aspect is great, but it does
seem like a burden to people writing once-off structs.

What I’d actually like to see is for private structs to infer things
like Copy and for public structs to then require it to be explicitly
stated. I don’t know how to do this in a way that’s not confusing
though.

-Kevin
_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev
Reply | Threaded
Open this post in threaded view
|

Re: Opt-In Built-In Traits

David Henningsson


On 2014-07-24 16:30, Kevin Ballard wrote:

> On Wed, Jul 23, 2014, at 12:52 PM, David Henningsson wrote:
>>
>>
>> On 2014-07-21 19:17, Patrick Walton wrote:
>>> On 7/21/14 8:49 AM, Tobias Müller wrote:
>>>> Patrick Walton <[hidden email]> 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.
>>>
>>> * 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.
>>
>> I find these arguments compelling, but if what we want to accomplish is
>> a conscious choice between copy and move every time somebody makes a new
>> struct, maybe "#[Deriving(Data)] struct Foo" vs "struct Foo" is not
>> first-class enough.
>>
>> Maybe the move vs copy should be done by using different keywords, a few
>> brainstorming examples:
>>
>>    * "datastruct" for copy, "struct" for move
>>    * "simplestruct" for copy, "complexstruct" for move
>>    * "struct" for copy, "class" or "object" for move
>
> What would this solve? Nobody who’s using a type is going to care about
> the keyword used to introduce the type, they’re only going to care about
> the behavior of the type. Using `datastruct` instead of `struct` will
> have zero impact on the people writing
>
> let x: Foo = y;
>
> Actually, the whole notion of having to intentionally describe on every
> struct whether you want it to be Copy is my biggest objection to opt-in
> traits. The API Stability / documentation aspect is great, but it does
> seem like a burden to people writing once-off structs.

Is it the typing or the decision that would be a burden? My idea was
mostly to reduce the typing compared to writing "Deriving(Data)" all the
time.

> What I’d actually like to see is for private structs to infer things
> like Copy and for public structs to then require it to be explicitly
> stated. I don’t know how to do this in a way that’s not confusing
> though.

That's actually an interesting idea. Maybe something like this?

struct foo1 {} /* Ok, copy or move is inferred */

#[Deriving(Data)]
pub struct foo2 {} /* Ok, copy behavior advertised */

#[Deriving(NoCopy)]
pub struct foo3 {} /* Ok, move behavior advertised */

pub struct foo4 {} /* Compile error, move or copy behavior must be
explicitly stated */

  // David
_______________________________________________
Rust-dev mailing list
[hidden email]
https://mail.mozilla.org/listinfo/rust-dev