Q: return statement causes Rust to seemingly have a mutable and shared reference at the same time I have the following code :
fn main() {
    let mut v = vec![1, 2, 3];
    for _ in v.iter() {
        v[0] = 0;
        // return;
    }
}

It does not compile, and I see why, since I'm attempting to have a shared (v.iter()) and mutable (v[0] = 0) reference of v at the same time.
However, uncommenting the return statement makes this code compile, and I'm not sure why. I guess that the compiler somehow manages to know that the shared reference to v can be dropped before the mutable borrow occurs, but how exactly does it knows that ?
In this exemple, what would be the lifetimes of the two borrows of v ?

A: A for loop is just syntactic sugar for a loop with a manual iterator:
for x in c {
   //...
}

is equivalent to:
let mut __iterator = c.into_iter();
while let Some(x) = __iterator.next() {
   //...
}

So for your original code that would be:
fn main() {
    let mut v = vec![1, 2, 3];
    let mut __iterator = v.iter().into_iter();
    while let Some(_) = __iterator.next() {
        v[0] = 0;
        // return;
    }
}

(The call to into_iter() is redundant here, because using that with an iterator just returns itself.)
This code fails just like yours, as expected.
But if you uncomment the return, then the compiler knows that the loop never repeats, so it is transformed as if:
fn main() {
    let mut v = vec![1, 2, 3];
    let mut __iterator = v.iter().into_iter();
    if let Some(_) = __iterator.next() {
        v[0] = 0;
    }
}

Now, non-lexical lifetimes (NLL) kick in, and sees that iterator is not used beyond the call to next() so it can be dropped just there. The _ does not count because it is not a real bind, but even if you wrote _x instead, NLL would drop it immediately as it is not used further. Thus v is no longer borrowed and the assignment to v[0] is perfectly safe and legal.
If you keep the original borrow of v alive it will fail again:
fn main() {
    let mut v = vec![1, 2, 3];
    let mut __iterator = v.iter().into_iter();
    if let Some(x) = __iterator.next() {
        v[0] = 0; // error! v is still borrowed
        dbg!(x);
    }
}

This fails with a nice explanation:
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
 --> src/main.rs:5:9
  |
3 |     let mut __iterator = v.iter().into_iter();
  |                          -------- immutable borrow occurs here
4 |     if let Some(x) = __iterator.next() {
5 |         v[0] = 0;
  |         ^ mutable borrow occurs here
6 |         dbg!(x);
  |              - immutable borrow later used here

