Comment by Klonoar
16 hours ago
If you understand the borrow checker, closures are just not that much on top of things.
In fact I can’t remember the last time I had to fight with them.
16 hours ago
If you understand the borrow checker, closures are just not that much on top of things.
In fact I can’t remember the last time I had to fight with them.
Closures are pretty simple in relation to their captures lifetimes, but they do have a lot of complexity in how the lifetimes of their argument and return type are computed. The compiler has to basically infer them, and that can easily go very wrong. The only reason it works most of the time is because closures are immediately passed to functions whose trait bound specify the expected signature of the closure, but once you deviate a little bit from the common case things start to break down. For example if the bound is `F: SomeTrait` where `SomeTrait` is implemented for `FnOnce(&' i32) -> &i32` the inference will break. Similarly if you store the closure in a local variable before passing it to the function. This used to come up pretty often for "async" closures that were supposed to take a reference as input, since it's impossible to specify their correct trait bound using directly the `Fn*` traits. There are a bunch of related issues [1] in the rustc repo if you search for closure and higher ranked lifetimes.
[1]: https://github.com/rust-lang/rust/issues?q=is%3Aopen%20is%3A...
I really wanted just yesterday to create a dyn AsyncFnMut, which apparently still needs async-trait to build the stable. but I was pretty much unable to figure out how to make that work with a lambda. saying this is all trivial once you understand the borrow machinery is really understating it.
> saying this is all trivial
The comment above isn't saying that closures are trivial. Once you understand the borrow checker, you understand that it's a miracle that closures in Rust can possibly work at all, given Rust's other dueling goals of being a GC-less language with guaranteed memory safety despite letting closures close over arbitrary references. Rust is in uncharted territory here, drawing the map as it goes.
Async is the stuff that messes up everything. Closures are not complicated.