News for hackers. Stuff that matters.
Re:Getting Past C by sirclueless on Tuesday January 03, 2017 @09:19PM (#13315156)
> Neither C nor C++ knows, at the language level, the size of an array, unless that size is fixed.

Neither does Rust.

> The subscript checking variants of C and C++ have to use "fat pointers" which carry along size information.

So do Rust's Slices.

> The overhead for this is large and nobody uses that.

People use std::vector all the time for this purpose in C++. It has about the performance you'd expect, with very little overhead except where you want it in bounds-checking.

I don't think there's actually a performance difference here. Rust's default is safer because it requires dropping to unsafe code to do something dangerous, but the same optimizations are available in both.

Re:Getting Past C by steveklabnik on Tuesday January 03, 2017 @10:02PM (#13315327)
One thing that I've heard might be a difference, but haven't confirmed yet: Rust's lack of move constructors. So you have a vector, it's full, you push one more. It has to reallocate. How do you copy all of the elements over to the new allocation? In Rust, it's a straight memcpy of T * n bytes. But due to move constructors in C++, IIRC they must be moved one at a time.

Again, I haven't actually dug into this; maybe someone more knowledgeable about this can point me in the right direction here?

Re:Getting Past C by Manishearth on Tuesday January 03, 2017 @10:33PM (#13315456)
This is correct. I suppose if you could get folks to mark all memcpy move ctors explicitly with a macro instead of relying on the default you could specialize std::vector's move with sfinae. Bit hacky. It already specializes for pod types though.

Lack of move and copy ctors in rust greatly simplifies things like this, and makes it very explicit when code is running, but the trade-off is that intrusive data structures are hard to do on the stack in rust.

Re:Getting Past C by rustmemcpy on Tuesday January 03, 2017 @11:38PM (#13315719)
How would a self-referential object work in Rust in that case? The move or copy constructor could not be a simple memcpy.

The self-reference would point to the old object. See this for an example: http://ideone.com/sEFtbN

Re:Getting Past C by Manishearth on Wednesday January 04, 2017 @12:22AM (#13315872)
https://github.com/Kimundi/owning-ref-rs exists, but basically you just don't do intrusive datastructures on the stack in rust.

(Intrusive datastructures on the heap are doable with some tricks.)

They're not very essential so it works out.

Re:Getting Past C by sirclueless on Tuesday January 03, 2017 @11:55PM (#13315786)
See steveklabnik's detailed answers elsewhere in this thread, but the short answer is that you can't write this data type in Rust.
Re:Getting Past C by sirclueless on Tuesday January 03, 2017 @10:31PM (#13315448)
Well, for trivially copyable types[1] the reallocation can be a straight memcpy. For the rest, I don't know that having or not having a move constructor is the important distinction; it will be preferred over the copy constructor if it is declared as not throwing exceptions, but either way some constructor of the object must be called if it exists (though it might be inlined and optimized away).

I imagine Rust does something similar, copying bytes if the underlying type has the `Copy` trait and calling some actual code if not, but I'm not familiar with the details.

[1]: http://en.cppreference.com/w/cpp/concept/TriviallyCopyable

Re:Getting Past C by Manishearth on Tuesday January 03, 2017 @10:38PM (#13315471)
No, all moves in rust are memcpy if not optimized out entirely. Rust has affine types so moves don't need to "invalidate" the source value at runtime, the compiler just doesn't allow you to use the source variable after a move.

Rust's answer to copy ctors is Clone, which is always explicitly called. Variable use in rust is a move. Trivially copyable types (Copy) will be copied without compile-time invalidating the old type.

Re:Getting Past C by steveklabnik on Tuesday January 03, 2017 @10:33PM (#13315457)
Thanks for elaborating on this!

> copying bytes if the underlying type has the `Copy` trait and calling some actual code if not,

It does not. Moves and copies are both "memcopy these bytes", the only difference is if you can use the previous copy or not. (This is also, of course, subject to the optimizer, which may elide the copy.)

> either way some constructor of the object must be called if it exists (though it might be inlined and optimized away).

Yeah, this is what I was getting at; this has to happen in C++, but not in Rust. You are right to point out that this only matters for things that aren't trivially copyable.

Re:Getting Past C by sirclueless on Tuesday January 03, 2017 @10:45PM (#13315508)
Interesting. How does Rust handle types that want to do interesting things when copied, like bookkeeping or updating internal pointers? Maybe you just can't, which would preclude some kinds of intrusive data structures, or owning non-copyable mutexes for example. Does `drop()` get called on objects that have been copied from?
Re:Getting Past C by steveklabnik on Tuesday January 03, 2017 @10:53PM (#13315532)
You pretty much just can't; that's what Manish was referencing above about this kind of thing being awkward.

It would be cool to have those things, but it also means that there's less "magic" stuff going on, which is nice. And it makes the semantics of stuff like this a lot simpler.

> Does `drop()` get called on objects that have been copied from?

Nope. In fact, Copy types can't have a Drop at all, but types that move don't have their Drop impl called when they move.

Re:Getting Past C by sirclueless on Tuesday January 03, 2017 @11:09PM (#13315613)
Thanks for all the responses, I'm learning a lot. This explains to me why iterators and other references to the internals of a data type have to take ownership of the whole data type, which is something I ran into several times during my (brief) explorations with Rust.
Re:Getting Past C by steveklabnik on Tuesday January 03, 2017 @11:22PM (#13315662)
Totally. There's one other interesting subtlety you might find interesting here, and that's self-referenceing structs. So for example,

struct Foo { s1: String, s2: &str, }

where s2 is always intended to point at s1's backing storage. What's unfortunate here is that Rust will disallow this, as it doesn't understand that s2 is pointing to some data on the heap, with a stable address, not the parts of the String struct in s1 that are part of the struct itself. So what this means is, in plain Rust, this type isn't movable, Rust is concerned about the invalidation.

However, you can get around this restriction with some unsafe code to teach Rust about it; this is the premise of the "owning-ref" crate.

Re:Getting Past C by Manishearth on Wednesday January 04, 2017 @12:20AM (#13315867)
> have to take ownership of the whole data type,

This isn't the case. They only need to borrow it. A borrowed value isn't allowed to move (the borrow itself can be copied around and shared within the scope of the borrow), so that works out.

Most Rust iterators only borrow the container or iterator they operate on. It's only explicitly moving iterators like .into_iter() (which extracts elements by-move) that don't.