Do you have any examples of the “perfectly safe code the Rust compiler will nag you about”? Not trying to start language wars, just genuinely curious as someone who writes Rust on occasion.
if (i != j)
swap_items(&mut arr[i], &mut arr[j]);
A contrived example and obviously the same can be achieved in many other ways, most of which the compiler would be happier about - but that's often the case with Rust: a seemingly safe thing isn't quite safe enough for the compiler so you have to do it differently. And that's the main problem of ergonomics in the borrow checker imo.This is helped enormously by helpful error messages, and there is great progress on fixing little paper cuts and improving the borrow checker to make more valid programs accepted by the borrow checker. But it doesn't contain a massive AI or theorem prover so there will always be situations where you'll need unsafe despite not actually being unsafe, or when you'll do something a bit more contrived than you might have expected.
enum Inner {
A(i32),
B(i32)
}
enum Outer {
Foo{
field: Inner
}
}
fn do_foo(val: &mut Outer) {
match val {
Outer::Foo{field: f @ Inner::A(id)} if *id == 3 => {
*f = Inner::B(25);
},
_ => {}
}
}
The compiler is seeing the `id` and `f` references as overlapping for the entire arm, even though the use of `id` and `f` are not interleaved. Bearing in mind that I don't actually know how the compiler works here, but I don't think this is a borrow checker limitation in and of itself, rather what I think is happening is that in the match expression the compiler is creating both `id` and `f` directly from `val`, creating the overlapping borrow.The reason I believe that is that this equivalent code results in the same error[1]:
fn do_foo(val: &mut Outer) {
let f = val.get_inner();
let id = val.get_inner().get_a();
if *id == 3 {
*f = Inner::B(25);
}
}
Whereas if you create the `id` reference from `f` instead of from `val` the compiler accepts it because `f` is not used between `id`s creation and death[2]: fn do_foo(val: &mut Outer) {
let f = val.get_inner();
let id = f.get_a();
if *id == 3 {
*f = Inner::B(25);
}
}
[0] Playground link: https://play.rust-lang.org/?version=stable&mode=debug&editio...
[1] https://play.rust-lang.org/?version=stable&mode=debug&editio...
[2] https://play.rust-lang.org/?version=stable&mode=debug&editio...Not an example, but if the borrow checker approved every proper program in a reasonable time, then it could solve the halting problem.