19 ms·
It's actually not that painful. C++ has a bit of a different safety model that somewhat relies on move constructors to work. Rust builds this in, with some cru
by Manishearth 9y ago
It's actually not that painful.
C++ has a bit of a different safety model that somewhat relies on move constructors to work. Rust builds this in, with some crucial changes.
For one, all C++ values are forced to have a zero state; e.g. when you move out of a unique_ptr (in certain ways, not all of them) the old one must be zeroed, because the destructor will still be run on it. Use after move leads to unspecified but not necessarily undefined. Rust OTOH does not have null as a valid value for its pointers. In case of conditional moves Rust will use an extra bit on the stack called a "drop flag" to track whether or not the destructor needs to be run, but only when needed, and usually it isn't so this complexity is abstracted away and also doesn't impact the runtime for the majority of times it isn't necessary.
99% of the move constructors in C++ are focused on making this work correctly. It's not necessary in Rust.
There are the 1% that do stuff like the small string optimization. Yes, you can't do this as neatly in Rust. But it's worth noting that move constructors have a significant cost, both in runtime (things like vectors need to deal with this correctly -- in particular vector<smart_object<_>> can't memcpy on resize anymore), and in compile time -- if you want to build good generic abstractions you have to guard against the fact that moves (and copies) can do anything. This would completely destroy Rust's ability to easily compartmentalize safety because now you have to worry about arbitrary code running every other line of your generic code. In C++ you don't care about this as much; since compartmentalizing safety is more of a best-effort thing and blame is harder to assign, whereas in Rust your generic datastructure must be safe when you throw any (safely implemented) type at it.
So yeah, sounds painful, but not really, and from a Rust POV what C++ has in this space is really painful.
- mehrdadn 9y ago> 99% of the move constructors in C++ are focused on making this work correctly. It's not necessary in Rust. There are the 1% that do stuff like the small string optimization. Yes, you can't do this as neatly in Rust. Hm, so let's say had this binary tree in C++: template<class T> class Node { Node(Node const &) = delete; // don't worry about copying for now Node &operator =(Node const &) = delete; unique_ptr<Node> a, b; Node *parent; T value; public: Node(Node &&other) : a(move(other.a)), b(move(other.b)), parent(move(other.parent)), value(move(other.value)) { if (parent) { if (parent->a == &other) { parent->a = this; } if (parent->b == &other) { parent->b = this; } } other.parent = NULL; } } You're saying I... can't have my binary tree in Rust?
- Manishearth 9y agohttps://doc.rust-lang.org/std/collections/struct.BTreeMap.html https://doc.rust-lang.org/std/collections/struct.BTreeMap.ht... you can't implement it _this_ way, but you can still implement a binary search tree (Btreemap is a b-tree, which is a generalization of a binary tree). Normally in Rust for a tree like structure with parent pointers you'd either use unsafe code or use Rc/Weak, depending on what you need. As far as binary search trees are concerned IIRC you can implement them without parent pointers, you'd move your relocation method to something called on the parent node.
- mehrdadn 9y agoOh, the parent pointer (or even the BST nature) wasn't really the point. The parent pointer was just an example that was easy to understand. For BSTs specifically, in C++, IIRC they use threading (= sibling pointers) to allow iterators O(1)-time access to the siblings in constant space, and those would have the same problems. And I didn't even mean to limit this to the BST case (I edited my comment while you were probably typing yours so you probably saw the version where I mentioned BSTs). In other non-BST binary trees you may well need a parent pointer for good time/space complexity, not merely find it convenient. Note that this isn't just limited to trees. Even doubly-linked lists would have this issue, as would other more-complex data structures. I guess my larger point is, it seems like the repercussions aren't just limited to missing low-level optimizations like small-string optimization like you claim, but they extend to your entire global object model as well as to what operations & time complexities you can support without the compiler yelling at you. That seems quite painful to me. Who wants to constantly argue with the compiler as to whether or not he should be allowed to have parent/sibling/other pointers?
- viraptor 9y ago> Who wants to constantly argue with the compiler as to whether or not he should be allowed to have parent/sibling/other pointers? People who value "I'm sure this can't explode", over "I'm pretty sure it won't explode, but at least it's a fast template that can stack-allocate" ;-) But seriously - you can still do a lot of that if you really need to for some reason. Either use an external library in C++ or write some unsafe code which won't restrict your pointers.