Uncommenting the line: //*ref_a = 55;
the compiler reports the following:
"cannot assign to `*ref_a` because it is borrowed
`*ref_a` is assigned to here but it was already borrowed"
to be more precise he adds:
*ref_a` is borrowed here: let result = reassign(&mut ref_a, &mut ref_b);
borrow later used here: *result = 77
;
Why ?
I also tried to analyze it step by step also with ChatGPT but in the end it itself and sent me here
I guess you understand the intent of this code and it doesn't matter that it's not the best way
to obtain it, I repeat: I'm playing/experimenting with Rust to try to better understand some of its peculiarities (and I'm definitely also influenced/deviated by the legacy of other languages) but especially with Rust, more which in other languages, I find myself in this situation: https://www.youtube.com/watch?v=yqp3KXDu9qE.
And I'm not Feynman, for sure.
The expression &mut _a creates a borrow that lasts for lifetime 'l1.
The expression &mut ref_a creates a borrow that lasts for lifetime 'l2.
Now, we get 'l2 ≤ 'l1 because otherwise &mut ref_a would be valid when ref_a is no longer valid, which is clearly problematic.
However, when you call reassign, since it reuses the same lifetime 'a for both 'l1 and 'l2, and this introduces the constraint that 'l1 ≤ 'l2. Combined with the previous constraint, we get that both lifetimes must end at the same time.
Now, you have a line containing *ref_a = 55. This assignment is only possible if 'l2 has ended when you get to this line, since 'l2 is the duration in which ref_a is borrowed, and ref_a must not be borrowed for you to assign to it.
However, the line *ref_a = 55 also requires that 'l1 has not yet ended. This is because ref_a originates from a borrow of _a, and if _a is no longer borrowed, then we can't use the borrow anymore.
So you now have a line where 'l2 must already have ended, but 'l1 can't have ended yet. Since you also require that they end at the same time, you get a compilation failure.
You should not ever use the same lifetime parameter for a mutable reference (p1: &'a mut ...) and any part of its referent (... &'a mut i32), because that ends up implying that the thing is borrowed for the rest of its own existence.
You need to let the inside and outside be separate lifetimes:
But then you'll find that the function doesn't compile. This is not because the lifetime annotations are wrong — they accurately reflect what it's trying to do — but because you may not duplicate a mutable reference (ending with both *p1 and *p2 containing the mutable reference that was initially contained by *p2), nor leave *p2 moved-out-of.
If you're OK with swapping the two references, then you can do that with a standard library helper function:
Note that the returned lifetime is &'b mut i32, not &'a mut i32. This is again because of the rule that you can't duplicate a mutable reference: if it was &'a mut i32 then you could use it along with *p1 (that is, ref_b) once the function returned, but the 'b lifetime from the borrows means that (from the outside view) ref_b will be borrowed-and-therefore-unusable until you drop result.
Due to personal issues, I had to interrupt the process, but I will resume it ASAP and I don't want this thread to be closed for now.
Some of you have made observations that, as far as I'm concerned, have unleashed the Titans from Tartarus.
And of course thanks for the reply