You've met the borrowing rules piecemeal. Let's state them cleanly and meet the part of the compiler that enforces them: the borrow checker.
The two rules
One: at any given time you can have either one mutable reference or any number of immutable references. Two: references must always be valid — they can never outlive the data they point at. That's the whole borrow checker, in two sentences. Everything else is the compiler proving your code obeys them.
References live until last use, not end of scope
Modern Rust is smart: a reference's borrow ends at its last use, not at the closing brace. So you can create immutable borrows, finish using them, and then take a mutable borrow in the same scope. This is called non-lexical lifetimes (NLL), and it's why a lot of code that looks like it should fail actually compiles.
The borrow checker is a prover, not a linter
It doesn't guess or warn — it proves, for every reference, that the data outlives it and that the aliasing rules hold. If it can't prove safety, it rejects the code. That's why a Rust program that compiles has no use-after-free and no data races: not 'probably,' but proven.
Fighting it is a phase
Early on, you'll feel like you're fighting the borrow checker. Later, you realize it was catching real aliasing hazards you couldn't see. The rules don't change; your intuition catches up to them. That shift is the whole reason this quest frames the compiler as a mentor.