I lost a whole afternoon to the borrow checker this week, and the worst part is that it was entirely my fault. I knew it was my fault about ten minutes in, and I kept fighting anyway, because surrendering to a compiler feels like surrendering, and I am occasionally an idiot about that.
The setup: I had a struct that held a Vec of things, and I wanted another field in the same struct holding a reference into that Vec. A self-referential struct. Coming from C, this is a Tuesday. In Rust it is a brick wall, because the moment that Vec reallocates, your reference is pointing at freed memory, and the borrow checker knows this even when I've conveniently forgotten.
struct Parser {
tokens: Vec<Token>,
current: &Token, // a reference into our own tokens. nope.
}
So I did the things you do when you're losing. I reached for Rc<RefCell<T>>, which compiled and made me feel filthy, because I'd taken a clean ownership question and answered it with runtime reference counting and the promise of a panic later if I got the borrows wrong. Then I tried lifetimes, sprinkling 'a everywhere with the optimism of a man who doesn't really understand what he's writing, and produced an error message long enough to need scrolling.
The graceful loss, when I finally took it, was to stop storing the reference at all. I stored an index into the Vec instead of a borrow.
struct Parser {
tokens: Vec<Token>,
current: usize, // an index. boring. correct.
}
An index can't dangle. If the Vec reallocates, the index still means the same logical position. It costs a bounds check on access, which I will never measure and neither will you. The compiler stopped complaining instantly, because I'd stopped asking it to bless something genuinely unsound.
That's the pattern, really. The borrow checker isn't being awkward for sport. When it digs in this hard, it's usually telling you the ownership in your head doesn't match the ownership in your design, and the fix is almost never to fight harder. It's to change the design so the question you're asking has a safe answer. Indices instead of self-references, owned data instead of borrows, a clearer notion of who owns what.
I'd like to say I learned this gracefully. I learned it after four hours, a RefCell I'm glad nobody saw, and a lifetime annotation I had to delete in shame. But I did learn it, and the borrow checker, infuriating as it was, was right the entire time. It usually is.