3 ms·
> 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
by saghm 3y ago
> 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.
- legerdemain 3y agoI don't understand what you're saying. Are you saying that "lazy borrowing" is how the compiler works, or that it's just how you mentally model Rust for your own convenience? There is no "lazy borrowing" in the Rust compiler, as far as I know. 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}"); }
- saghm 3y agoThe "lazy borrowing" was just a personal mental model, not any claim about how things are implemented under the hood. That said, I think all your examples are hinging on very specific behavior from the `Deref` and `DerefMut` traits that might not apply generally. 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 https://news.ycombinator.com/item?id=39557697. [0]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=30c1c3fda19450fee4519eb49720e2a2 https://play.rust-lang.org/?version=nightly&mode=debug&editi... [1]: https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coercion https://doc.rust-lang.org/std/ops/trait.Deref.html#deref-coe... [2]: https://doc.rust-lang.org/std/ops/trait.DerefMut.html#mutable-deref-coercion 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)
- legerdemain 3y agoWould it be easier to convince you if I replaced String with a type that doesn't implement Deref or DerefMut? 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. 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 }
- saghm 3y ago