A very simple question about borrow

The first snippet can compile but the second cannot. Why? what is the difference?

fn f0360_ok() {
    let mut h = vec![1, 2, 3];
    assert!(h[0] == 1); //immutable borrow
    let m = &mut h;
    m.push(4); //mutable borrow
    assert!(m[3] == 4);
    assert!(h[0] == 1); //immutable borrow
// compile error. cannot compile
fn f0360_bad() {
    let mut h = vec![1, 2, 3];
    let m = &h; // immutable borrow。
    h.push(4); //mutable borrow
    assert!(h[3] == 4);
    assert!(m[3] == 4); //immutable borrow

the compile error is

4 |     let m = &h; // immutable borrow。
  |             -- immutable borrow occurs here
5 |     h.push(4);
  |     ^^^^^^^^^ mutable borrow occurs here
6 |     assert!(h[3] == 4);
7 |     assert!(m[3] == 4);
  |             - immutable borrow later used here

In the first snippet, there are no overlapping borrows. Apart from the mutable reference that you store in m, every other borrow is temporary.

In the second snippet, you store an immutable borrow in m, and you use it after trying to push on the vector. In between, you are borrowing the vector mutably, exactly by trying to push to it. That creates an overlapping mutable and immutable borrow.

I don't understand. h and m, are not overlapping borrows?

h is not a borrow, it's a value.

in the second snippet, the error information says: h is a mutable borrow

No, it doesn't say that. It says that the entire method call causes a mutable borrow of h, because Vec::push() has the signature fn push(&mut self, value: T).

In your first snippet, m is no longer alive after the line

assert!(m[3] == 4);

( By "no longer alive", I mean the compiler knows it is no longer used )