Indeed. My (and by no means am I the only one) mental model of closures is really simple. A closure is a struct with no name, and the free variables of the closure are fields in the struct. If the closure is annotated with 'move', then those fields are owned, otherwise the fields are borrowed and the lifetime parameter on the struct is the shortest of the bunch. Then, calling the closure is just calling it with 'self' (FnOnce), '&self' (Fn) or '&mut self' (FnMut).
Everything else pretty much falls into place.
The other thing that was added since you've looked at Rust is that you can return unboxed closures because Rust has gained the ability to express anonymous types directly by naming a trait it implements:
fn adder(x: i32) -> impl Fn(i32) -> i32 {
move |y| y + x
}
fn main() {
let add2 = adder(2);
assert_eq!(7, add2(5));
assert_eq!(10, add2(8));
}