> A closure is like an anonymous function. I like the article overall, but I have a bit of a pedantic note about this opening line. Using "like" suggests that there is some similarity that can be used to understand one thing from the other, but in truth these are two distinct (though related) technical terms, and I think phrasing this relationship as a simile may actually obstruct understanding rather than enable it.…
In some language all functions capture their environment, making that original statement more true than you make it out to be. Personally I think the term closure should be banned, because it only adds confusion. There are just functions, some named some anonymous, some capturing the environment, some not, some being disallowed by some constraints from the host language. The term closure cuts through this space in a…
A guide to closures in Rust
61–70 of 102 posts
Re: A guide to closures in Rust
#62> A closure is like an anonymous function. I like the article overall, but I have a bit of a pedantic note about this opening line. Using "like" suggests that there is some similarity that can be used to understand one thing from the other, but in truth these are two distinct (though related) technical terms, and I think phrasing this relationship as a simile may actually obstruct understanding rather than enable it.…
In some language all functions capture their environment, making that original statement more true than you make it out to be. Personally I think the term closure should be banned, because it only adds confusion. There are just functions, some named some anonymous, some capturing the environment, some not, some being disallowed by some constraints from the host language. The term closure cuts through this space in a…
In some pieces of art all quadrilaterals are squares, but this doesn't mean the two terms are somehow equivalent and therefore one of the terms should be done away with.
There is a consistent technical distinction between "functions" and "closures".
> There are just functions, some named some anonymous, some capturing the environment, some not
No. Functions do not capture environments. Functions don't know what environments are. If you're talking about functions and environments together, you're talking about closures. That's really all there is to it.
Re: A guide to closures in Rust
#63> A closure is like an anonymous function. I like the article overall, but I have a bit of a pedantic note about this opening line. Using "like" suggests that there is some similarity that can be used to understand one thing from the other, but in truth these are two distinct (though related) technical terms, and I think phrasing this relationship as a simile may actually obstruct understanding rather than enable it.…
I would further elaborate on your point by saying that as someone who likes to hang out on programming fora and help people as I was helped when I was young, this terminology confusion causes real confusion in the field as well. Many programmers-in-training find it confusing as to why whether or not a function has a name seems to be tied to whether or not it has an environment (not that they phrase it that way but th…
Some other conflations I see frequently in the PL space:
- "argument" vs "parameter"
- "type coercion" vs "type casting"
- "static typing" vs "strong typing"
- "function" vs "method" (and we can throw in "procedure" and "subroutine")
Re: A guide to closures in Rust
#64There's a lot to be said in favor of Rust's approach to memory management but closures in Rust suck compared to garbage collected languages.
True, but isn't the complexity of Rust closures essential rather than accidental? Fundamentally closures are easy in e.g. Go because you don't have to think about lifetimes, at all. As soon as you capture a variable, the GC guarantees it won't be dropped from underneath your feet. With non-GC'd languages that responsibility moves from the GC to the programmer. The trickyness of using closures in Rust seems largely to…
With those in mind, Rust's complexity does indeed look accidental here. It has its other benefits, but it does make closures a bit more difficult.
Re: A guide to closures in Rust
#65Earlier quoted context omitted.
If you want to do anything with the closure it gets a little tricker, for example if you did something naive like fn fn_that_takes_closure (f: fn (i32, 3i2) -> i32) { ... } let x = |a, b| a + b; fn_that_takes_closure(x); That would not compile, because `x` does not have the type `fn (i32, i32) -> i32` (that's reserved for "normal" functions). Every closure in Rust has a unique anonymous type, which means if you want…
> That would not compile It does compile. > because `x` does not have the type `fn (i32, i32) -> i32` (that's reserved for "normal" functions). Closures can cast to function pointers just fine as long as they don't capture anything.
According to the Rust reference:
> Closure types > > A closure expression produces a closure value with a unique, anonymous type that cannot be written out.
https://doc.rust-lang.org/reference/types/closure.html
Even a non-capturing closure is written using a closure expression and produces a unique, anonymous type.
Re: A guide to closures in Rust
#66Earlier quoted context omitted.
> That would not compile It does compile. > because `x` does not have the type `fn (i32, i32) -> i32` (that's reserved for "normal" functions). Closures can cast to function pointers just fine as long as they don't capture anything.
Closing over variables is the thing that makes it a closure. Otherwise, you just have an anonymous function. A closure is a function plus the captured environment. The difference is meaningful here. You have to allocate a closure (and deal with its lifetime and the lifetimes of the variables it references) but the anonymous function is just a pointer to static code in the binary. That's the entire difficulty with clo…
Also, your arguments only partly apply in Rust. Rust doesn't heap-allocate closures. And you also often don't have to deal with lifetimes - a closure that captures variables by move or copy is perfectly self-contained
The difference between a closure that captures and a closure that doesn't is like the difference between `(T)` and `()` - same kind of thing, so it adheres to the same terms and behaviors
Re: A guide to closures in Rust
#67Why do I need to see machine-specific types like i32 in a lecture about closures?
Re: A guide to closures in Rust
#68Earlier quoted context omitted.
The borrow checker rejects many otherwise valid programs. So if you do your job in correctly in C/C++, the borrow checker might accept the Rust equivalent or it might not.
Maybe that was true in 2017, but today borrow checker and compiler in general has covered so much of the Rust design space that the "correct programs" it rejects are more like rejecting Duff's Device kind of code. You are more likely to find yourself implementing low-level data structure or device interface where you need raw pointers tightly localized in the unsafe{} scope.
Since 2017 it certainly admits quite a few more programs, but it's still pretty easy to find things that require a different approach than they would in C or C++.
Re: A guide to closures in Rust
#69Earlier quoted context omitted.
> That would not compile It does compile. > because `x` does not have the type `fn (i32, i32) -> i32` (that's reserved for "normal" functions). Closures can cast to function pointers just fine as long as they don't capture anything.
Closing over variables is the thing that makes it a closure. Otherwise, you just have an anonymous function. A closure is a function plus the captured environment. The difference is meaningful here. You have to allocate a closure (and deal with its lifetime and the lifetimes of the variables it references) but the anonymous function is just a pointer to static code in the binary. That's the entire difficulty with clo…
That's incorrect, a closure in Rust compiles down to a static function that takes its environment as an argument. None of that requires a heap allocation in the above code.
Re: A guide to closures in Rust
#70Earlier quoted context omitted.
"And sure, plenty of people will tell if you do it "right" you'll have no issues." My answer to that is always that if you are able to do it right in C or C++ (and maybe golang) you should not run into any issues with Rust. Especially you should not ever have to fight to borrow checker because it does the job you should be doing as a C/C++ programmer in your head anyway.
>"...it does the job you should be doing as a C/C++ programmer in your head anyway." False. Too many false positives
It does so better than you can in that it doesn't have false negatives anywhere you're sufficiently playing by its rules.
You can also do so better than it can, in that you can see reasons things are okay that it can't understand.
I agree that there are too many false positives to assert that a competent C or C++ programmer doesn't have learning to do to appease (and leverage) the borrow checker.