This is only partially true. If you hold a mutable reference to a field in a structure, you can't get a reference to the entire structure. (Think of a mutable reference as a read-write lock, because that's what it is, just at compile time / type level instead of at runtime.) However, you can get a mutable reference to other fields in the structure. And an immutable reference to a field (analogous to a read-only / shared lock) does not prevent other immutable references, so you can totally get a reference to the structure while a reference to one of its fields is outstanding.
Here's an example of this on play.rust-lang.org: http://is.gd/ySSsex
That compiles and runs with the extra scope around the mutable borrows. If you comment out that scope, then yes, it won't compile.
My experience, coming to Rust from C++ and C, is that a lot of real-world C++ and C code is imprecise about mutability and shared references, and relies on the programmer not doing anything particularly weird. It's true that in this example, nothing would go wrong by omitting the braces because the references g and h aren't actually used. But in larger and legacy codebases, it's very easy to forget the mutability rules that were in the head of the previous programmer. So directly porting C++ code is going to be annoying, but that's mostly because the hard work is figuring out what was implicit in the C++.
> seemingly arbitrary rules
Most of them are about Rust's core guarantee: data race freedom. Some of them are due to a certain conservativeness of any static analysis, and may be relaxed in the future. > for the love of all that is holy, prove me wrong
It depends on exactly what you're doing. There are always ways to get around things, but it can depend on knowing Rust and its standard libraries well. As a younger language, some patterns are still being developed, and aren't always as obvious as they could be. We'll get there...Rust is certainly a different language, and if you try to port C++ code directly over, you may have problems. Such is life. :)
IMO this is just a part of it (and I think you agree, based on previous conversations). The actual thing is that the rules enforce a discipline about data, similar to the discipline in functional languages (except here it's allowing sharing XOR mutation instead of forbidding mutation entirely). This discipline gets us many things -- memory safety, safety from iterator invalidation-y things (there's a whole class of memory safety bugs that happen when you modify the exterior of a things whilst holding a pointer to the interior -- from iterator invalidation to invalidating pointers to a vector after truncation to invalidating enums), and clarity in code. Whilst the chronology of it's design may not be such, I personally look at data race freedom as something we got for free from this discipline, instead of the core focus of it.
Put another way: Rust isn't aiming to be top of the pack in terms of enforcing a certain programming style, where as it is aiming to be top of the pack in terms of safe systems programming. (It might happen to be the best language for the former, but that is a consequence of the latter, not the other way around.)
I don't think that Rust is trying to test out a programming paradigm or whatever, I'm saying that these "seemingly arbitrary rules" get us a lot of things, by proxy of a certain paradigm, and reducing it to "data race freedom" (when it's so much more) is something we should avoid.
(My comment seems to focus on the discipline, I was just using it as a proxy for all of the things it gets us)
Furthermore, I still think the best phrasing of the rules is for that guarantee: if a rule is removed one can usually construct fairly simple programs that have data races/memory unsafety. Of course, it is definitely true that the arbitrary rules have other benefits, but if there was a simpler scheme that gave the core memory safety without the other things, I think Rust would've adopted it.
I think conservative approach to fundamentals is a great way to build a robust language and the way it's presented to me suggests that's one of the objectives; however, for an outsider with experience in other, more lenient (and, obviously, bug-prone) languages, those constraints might appear too restrictive. I believe it's a transitional feeling though, hence calling them 'seemingly' arbitrary.
It's a really bad idea to try and write stuff in a "C++ way" with tons of mutation, Rust encourages a rather different discipline in handling data which is at odds with the regular C++ style of programming. But this takes time to pick up. Once you've programmed with it for a while it feels pretty natural, though, especially since it's very easy to reason about data in this model.
Here's why Rust has this model: http://manishearth.github.io/blog/2015/05/17/the-problem-wit...
As geofft mentioned below, you can get mutable references to multiple fields if you want.
In more complex situations, use Cell<T> (for copyable types, this is zero cost though it can prevent some optimizations) or RefCell<T> (this works for any type, but has a slight cost) for more fine-grained mutability control. You shouldn't need these often, but they exist