A refactor that breaks this ordering results in code that obviously shouldn't work in the eyes of any non-beginner rust programmer.
A refactor that breaks this ordering results in code that obviously shouldn't work in the eyes of any non-beginner rust programmer.
fn main() {
let mut s = String::from("s");
let mut1: &mut _ = &mut s;
let mut2: &mut _ = &mut *mut1;
*mut2 = String::from("t");
println!("using mut2: {mut2}");
println!("using mut1: {mut1}");
}
Some people use a mental model of reference lifetimes that allows "discontinuous lifetimes" where the valid region has holes. I don't think that's how the compiler models reborrowing, and even under that model, `mut1` is created before `mut2` and gets dropped after `mut2`.I think what you're calling "holes" are what I'd just consider to be "lazy" borrowing, where the reference doesn't really matter until the first time it's dereferenced. The only use of mut1 that happens before the end of mut2's lifetime is to assign a reference mut2 to a value that is never used. In other words, mut1 is never actually dereferenced while mut2 exists. I don't consider `&mut *` as dereferencing because it's just syntax for saying "make a new mutable reference to the same thing", which is necessary because mutable references don't implement Copy or Clone. If you instead used `let mut2 = &mut s;`, it would be abundantly clear; they don't really overlap in any way that matters because nothing would change if mut1 didn't exist at all until after mut2 no longer needed to.
You can create a mutable reference `mut1`, mutate through it, create a new mutable reference `mut2`, mutate through it, then mutate through `mut1` again. Here is an example to illustrate:
fn main() {
let mut s = String::from("1"); // create value
let mut1: &mut _ = &mut s; // create a ref mut
*mut1 = String::from("2"); // write through mut1
println!("using mut1: {mut1}"); // print mut1
let mut2: &mut _ = &mut *mut1; // create a second ref mut
*mut2 = String::from("3"); // write through mut2
println!("using mut2: {mut2}"); // print mut2
*mut1 = String::from("4"); // write through mut1 again
println!("using mut1: {mut1}"); // print mut1
}
Lifetime analysis can infer shorter lifetimes for unused refs. But creating refs in the wrong order matters even if you never use them, and not all orders are valid Rust programs. Here's a very small example that doesn't compile: fn main() {
let mut s = String::from("1");
let mut1: &mut _ = &mut s;
let mut2: &mut _ = &mut s; // never used!
println!("Using mut1: {mut1}");
}Looking at the generating MIR output[0] from your second example changed to use the deref syntax from the first one, it seems like the value of `mut1` is silently getting downgraded to `&[&str]` under the hood, presumably due to the fact that the rules for `Deref`[1] and `DerefMut`[1] allow the compiler to liberally substitute dereferences with calls to those trait methods, which crucially allows the compiler to change the result of dereferencing `&mut` to return a shared reference. This is actually almost exactly what was being suggested in the sibling comment here: https://news.ycombinator.com/item?id=39557697.
[0]: https://play.rust-lang.org/?version=nightly&mode=debug&editi...
[1]: https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coe...
[2]: https://doc.rust-lang.org/std/ops/trait.DerefMut.html#mutabl...
(edited shortly after posting to rephrase without needing to use deref operator in the in-line snippets because it messed up the italics of all of the text after like this)
fn main() {
#[derive(Debug)]
struct X(i32);
let mut s = X(1); // create value
let mut1: &mut _ = &mut s; // create a ref mut
*mut1 = X(2); // write through mut1
println!("using mut1: {mut1:?}"); // print mut1
let mut2: &mut _ = &mut *mut1; // create a second ref mut
*mut2 = X(3); // write through mut2
println!("using mut2: {mut2:?}"); // print mut2
*mut1 = X(4); // write through mut1 again
println!("using mut1: {mut1:?}"); // print mut1
}`&T` and `&mut T` implement Deref (and `&mut T` implements DerefMut) for all types[0].
> I do wish you had tried it yourself, because I hope your goal is to learn something about Rust and not just to argue down someone on the Internet.
I'm pretty confused about the sharp turn you took here. I happen to think your explanation of what's going on in the code you posted is incorrect, and if you truly are hoping that others here are trying to learn, you'd be more successful in helping them by not acting as if anyone who doesn't think you're correct is acting in bad faith. From my perspective, you completely misread what I said about Deref in my last comment and then didn't follow the advice you're giving about trying out the change I suggested with your own code because the same error occurs with the struct you define if you don't use the deref operator[1]. I'm open to the idea that I'm wrong about `Deref` being the cause of all this, but you don't really seem to be open the idea that you might be incorrect, which comes across as fairly arrogant given that you haven't really demonstrated that you understand that I'm talking about the `Deref` implementation on the _reference_, not the underlying value,.
[0]: https://doc.rust-lang.org/src/core/ops/deref.rs.html#164
[1]: https://play.rust-lang.org/?version=nightly&mode=debug&editi...
https://smallcultfollowing.com/babysteps/blog/2013/11/20/par...
https://haibane-tenshi.github.io/rust-reborrowing/
https://stackoverflow.com/questions/62960584/do-mutable-refe...
Meanwhile, you are insisting on reasoning about Rust from first principles and making lists of links to documentation that is, at best, tangentially relevant.
For example, the fact that `&T` implements Deref is not relevant here. Applying Deref::deref to a &T returns a &T. For most intents, it's a no-op.
I did know but forget that all variables are dropped at the end of a scope, not earlier. Borrows can however last for shorter times and as this example illustrates. They can be borrowed multiple times, as long as they are returned before the parent uses their borrow.