5

Closures as Anti-Lifetime-Gluteal-Bite-Device

 3 years ago
source link: https://llogiq.github.io/2015/08/19/closure.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Closures as Anti-Lifetime-Gluteal-Bite-Device

19 August 2015

Remember when reddit user The_Doculope warned me that “lifetime elision bites us in the ass”? It recently didn’t happen, but only narrowly, and today I want to share you the device I used to enact my escape: Closures.

The problem

Let’s say we want to do something with a borrow, but we need to get it first. So we write a function get_borrowed_foo(..) that gets us the needed foo. This will however not work, we will get an error (example modified from real code to protect the guilty):

src/foo.rs:548:30: 548:82 error: borrowed value does not live long enough
src/foo.rs:548                 &path.segments[0].identifier.name.as_str()
                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: in expansion of if let expansion
src/foo.rs:546:9: 550:10 note: expansion site
src/foo.rs:545:59: 552:6 note: reference must be valid for the lifetime 't as defined on the block at 545:58...
src/foo.rs:545     ...lots of code here...

So what can we do? We cannot return the borrow here, because its lifetime is tied to something we cannot control. But we do have a way of wrapping a function so that we can freely use our borrow within our function: Closures.

Instead of get_borrowed_foo(..), we write a with_borrowed_foo(..) method, like:

fn with_borrowed_foo<F, T>(path: &Path, f: F) -> T
where F: FnOnce(&Foo) -> T {
  f(&path.segments[0].identifier.name.as_str())
}

Now we can call it with just about any closure that takes an immutable reference to a Foo and returns whatever, wrapping it in our with-function will ‘lift’ it to work on &Path instead of &Foo.

Note that I haven’t invented the technique, I just stole it from the syntax::codemap::CodeMap::with_expn_info(..) function.

Bonus: Redditor SimonSapin had a great solution involving explicit lifetimes which unfortunately turned out not to work. Yet. A similar solution may however work to solve related problems in other circumstances.

I also should note that I recently came across some more scary-looking lifetime errors, which came after a simple erroneous statement that closed a scope early. So if you see lifetime errors, don’t blindly grab a closure, look if there are other errors and fix them first.


What techniques do you use to get around ownership / borrowing issues? Discuss on reddit and rust-lang users


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK