Building Understanding of Closures with Rust

A toy horse, and a rotund soldier with a helmet on are near a cardboard box

Closures are a neat tool that can resemble wizardry if used properly. My first thought when I first encountered the term was to fixate on the name. Why are they called that? Here is my favorite explanation so far, found on StackOverflow:

An analogy that might help beginners to this is a closure ties up all the loose ends, which is what a person does when they seek closure (or it resolves all necessary references, or ...). Well, it helped me to think of it that way :o) -Will Crawford

This doesn’t really help me understand what a closure really does, or how it works, but I feel like it’s the best way to contextualize WHY you’d use a closure.

Last bit of Pre-amble: In Rust, we have another concern as well, the compiler. Closures will help us when dealing with the borrow checker as we will gain permission to access memory in new and interesting ways that would otherwise be off-limits.

First, Go Write Closures Now

This Rust by Example set on Closures has all the code that is needed, and more examples from me are not going to improve that. So what can I say that the section doesn’t? Keep writing and tinkering in the editor with the closure until it clicks that it’s a sort of micro-verse for your variables.

To comfortably understand Closures, I had to write a few of them

I didn’t really properly comprehend what to do with closures. It’s been so rare in programming that I’ve encountered issues that couldn’t be solved with plain functions, classes, and their associated methods.

And to be honest, I typically try to avoid writing code in TypeScript or Python that leverages closures because it’s atypical for me, and having little ‘pocket universes’ of scope freaks me out a little bit.

That’s not true for Rust.

The ever-present and always forceful hand of the compiler makes Closures a very useful tool in Rust. It’s a way of leveraging the philosophy of working with the compiler instead of trying to shoe-horn ideologies that the compiler doesn’t abide. Working against the compiler is ill-advised and will result in a distaste for the language.

Understand the concept of Capture

Capture may be the key ingredient of comprehension of closures. Whatever the syntax or language, a closure exists as its own little context within a given scope. Because it contains all the references needed to do whatever functionality is contained within the closure, it’s basically its own little object.

More accurately, in the way an object encapsulates a context, the closure does a similar thing. And capture describes the act of bringing all the relevant references that the internally defined functions will need into the context of the closure.

Why is this different from a Struct with traits?

Closures can provide the ability for quick anonymous references to variables. With traits it’s quite a lot of boilerplate to type out the struct, type out its methods, and then populate that stuff creating new instances of everything everywhere to appease the borrow checker.

With a closure, you can quickly write a function that would do something similar to what a class instance with all its various knobs, gears, and wires, without all the boilerplate. Then if that same thing needs to be repeated over and over in the future it might be time to consider building structs and traits for that.

My preferred use cases?

I haven’t developed them yet. Outside of theory and practice, I’ve not used closures more than a handful of times over the past 10 years. A filter-sieve here, some kind of form validation there, I’ve never really developed a good set of goal-posts for when to use them. But I do think that because of understanding how the borrow checker treats variables and closures, I’m starting to see some possibilities where I can potentially start implementing them more frequently. Here are some ideas I want to test to see if I can find a place for closures in my work.

On-the-fly Object Design

Often there is the need to get some piece of work done quickly, and sometimes it feels necessary to craft an object with its own internal methods to do things. It could be useful to reveal exactly how complex a structure needs to be, or better yet, doesn’t need to be.

Context Wrapping Functions

Often when I have a chain of functions that need to work together in concert I’ll make those functions return a context wrapper instead of a simple result. That wrapper brings with it some of the information that was contained within that function to let me know what happened during the functions’ execution without going to it and creating breakpoints directly. I only use this in cases where there is ample memory, and the precedence goes to quick troubleshooting or simple maintenance, rather than raw performance.

Conclusion

After tinkering around with Closures in Rust I definitely better understand ways to focus on using closures more in other languages. I think that closure is a great way to satisfy the borrow checker in rust, and is yet another aspect of the language that aids the developer in working with the compiler rather than against it. This exercise in rust was definitely worth my time.

Previous
Previous

Soil Moisture Level Automation

Next
Next

First Grocery List Generated!