5 ms·
I think the first example is actually not very compelling for C++ programmers, because it's specific to vector. e.g. in C++ this is safe: deque<int> xs = {
by millstone 6y ago
I think the first example is actually not very compelling for C++ programmers, because it's specific to vector. e.g. in C++ this is safe:
deque<int> xs = {1, 2, 3};
int *x = &xs[0];
xs.push_back(92);
cout << *x;
but Rust would still reject this.
- flohofwoe 6y agoBut this is purely by convention isn't it? You as the programmer have to know that deque<> never changes item locations in memory under the hood like vector<> does, the compiler will not complain either way. There's nothing in the interface of vector<> or deque<> that somehow hints to the user or (more importantly) to the compiler that one randomly moves things around in memory and the other doesn't. That's what makes C++ so extremely memory-unsafe (IMHO it's even worse in C++ than C, because at least in C, dynamic memory management usually is right in your face and not hidden under a sugarcoating layer like nearly everywhere in the C++ stdlib).
- millstone 6y agoRight. Iterator invalidation rules are important yet only checked indirectly, through tools like ASAN or VC++ debug iterator support. It's a big problem. But Rust has its conventions too. Probably you would satisfy the borrow checker through an index instead of a pointer: let mut xs = vec![1, 2, 3]; let idx = 0; xs.push(92); println!("{}", xs[idx]); This is memory safe, but in an unsatisfying way. Nothing hints to the compiler that idx is connected to the vector. If you inserted into the front rather than the back, you would have a "dangling index". You just have to know to not do that. It's purely by convention too, except there's no tooling support to help. It turns out programs really do need a way to hold references to collection elements across mutation.
- deleted 6y ago[deleted]
- zbentley 6y ago> This is memory safe, but in an unsatisfying way. How is it unsatisfying? I know that "xs[idx]" isn't going to be garbage data from elsewhere in my program, and that it won't occasionally segfault when I try to print it. That's a big improvement.
- pornel 6y agoYes, this is controversial. Rust requires accepting that it operates on broad rules, and even changing design patterns to fit them. It's unacceptable to some C++ programmers. OTOH if you roll with this, it makes things simpler. You get uniform rules and guarantees for all interfaces. You don't need to evaluate correctness in detail on a case-by-case basis "is this code safe in this situation?". It also helps with fearless refactoring, since you can't have these unobvious dependencies on implementation details. You can change a VecDeque to a plain Vec, and if it still compiles, it's still safe.
- millstone 6y agoYeah, Rust effectively assumes and then enforces the weakest possible iterator invalidation rules. A C++ programmer may write: vec.erase(iter); and if the iterator is invalid or from the wrong vector, this will crash, if you are lucky. Rust won't compile that, so instead you would write: vec.remove(idx); This won't crash even if `idx` is from the "wrong vector", or refers to an element that has been moved. So we have improved memory safety, but it hides our mistakes. It would be really nice in both Rust and C++ to have validity checks here. The closest thing I know of is VC++'s debug iterator support. https://docs.microsoft.com/en-us/cpp/standard-library/debug-iterator-support https://docs.microsoft.com/en-us/cpp/standard-library/debug-...
- vlmutolo 6y agoRust rejects this because it isn’t necessarily safe. The Rust deque grows when it needs to, reallocating and copying all the old elements over to the new location, and then freeing the old location. If push_back triggers this reallocation, then you get UB here. So either the C++ deque doesn’t offer the same functionality, or it isn’t actually safe.
- millstone 6y agoThe C++ standard requires this to be safe. Internally deque is implemented as a vector of fixed-size blocks. Pushing an element may allocate a new block but will not deallocate an existing one.