ex3 case is obvious: you borrow whole f while calling f.get_a(), then add something to the result, then the borrow is no longer needed and may be forgotten so f may be borrowed again for assignment.
ex2 doesn't compile work, because get_a elided lifetimes to get_a(&'a self) -> &'a u16, so the f must be borrowed for at least a long, as returned result, so nothing from f may be borrowing until a is in scope. In this case you know, they only thing this function using internally is f.a, but it can't be deduced looking at function signature.
In ex1 you never borrow whole f - you are just borrowing distinct part of it.