So, I just finished Chapter 10 (Generic Types, Traits, and Lifetimes). There's paragraph from third part:
When annotating lifetimes in functions, the annotations go in the function signature, not in the function body. Rust can analyze the code within the function without any help. However, when a function has references to or from code outside that function, it becomes almost impossible for Rust to figure out the lifetimes of the parameters or return values on its own. The lifetimes might be different each time the function is called. This is why we need to annotate the lifetimes manually.
I do not understand it at all. Why it is almost impossible? Instead of using a function we could extract code from its body and use this code directly in place we call this function. Borrow checker still will be able to work and point dangle reference problem, if there any, and, at the same time, we don't have to specify lifetimes. Program, in fact, will be the same (except bigger and harder to read).
The borrow checker intentionally does not use function bodies. If it did, then changes to implementation of a function could unexpectedly break uses of the function. Even small changes to code could have a domino effect through calls in other functions, and a tiny change in one small function could break the whole program.
When the borrow checker checks only against function signatures, the function signature and documentation is also a stable guarantee what is allowed and what isn't. Incorrect changes inside the function are detected within the function, instead of escaping the function and cascading into lifetime errors somewhere else.
The same applies to type inference. It's function-local, so that one small change in one expression can't accidentally rewrite the whole program.
The only exception Rust made was for guessing Send/Sync of impl Trait and async fn, and these can cause mysterious errors at a distance.