Question

I'm trying to avoid repeating myself by using a closure in the following code:

fn add_raw(&mut self, pair: RawLinkPair) {
    let convert = |raw: &RawLink| {
        Link{
            id:         self.get_or_create(raw.name).id,
            flow:       raw.flow,
        }
    };

    println!("Hive received pair: {}", pair);
    let parent = convert(&pair.parent);
    let child = convert(&pair.child);
    self.link_concepts(parent, child);
}

That doesn't work. It gives me this error:

hive.rs:64:9: 64:13 error: cannot borrow `*self` as mutable because previous closure requires unique access
hive.rs:64         self.link_concepts(parent, child);
                   ^~~~
hive.rs:55:23: 60:10 note: previous borrow of `self` occurs here due to use in closure; the unique capture prevents subsequent moves or borrows of `self` until the borrow ends
hive.rs:55         let convert = |raw: RawLink| {
hive.rs:56             Link{
hive.rs:57                 id:         self.get_or_create(raw.name).id,
hive.rs:58                 flow:       raw.flow,
hive.rs:59             }
hive.rs:60         };
hive.rs:65:6: 65:6 note: previous borrow ends here
hive.rs:54     fn add_raw(&mut self, pair: RawLinkPair) {
...
hive.rs:65     }
               ^
error: aborting due to previous error

In this case I'm not actually saving myself too many keystrokes. I can type everything out by hand and it works fine:

fn add_raw(&mut self, pair: RawLinkPair) {
    let parent = Link {
        id:     self.get_or_create(pair.parent.name).id,
        flow:   pair.parent.flow,
    };

    let child = Link {
        id:     self.get_or_create(pair.child.name).id,
        flow:   pair.child.flow,
    };

    self.link_concepts(parent, child);
}

I understand the error (so I think), but:

  1. Is there something wrong with using a closure here in principal or did I just write this wrong?
  2. Is there a more idiomatic way to write this, or at least, how do I satisfy the borrow checker?
  3. This might be a dim question, but why doesn't the borrow just end immediately after the last time convert is called? It seems strange to me especially when I compare it to the bottom version, which ultimately does the same steps minus the closure.
Was it helpful?

Solution

You should be able to fix it like this (something you called "satisfy the borrow checker"):

fn add_raw(&mut self, pair: RawLinkPair) {
    let (parent, child) = {
        let convert = |raw: RawLink| {
            Link{
                id:         self.get_or_create(raw.name).id,
                flow:       raw.flow,
            }
        };

        (convert(pair.parent.clone()), convert(pair.child.clone()))
    };
    self.link_concepts(parent, child);
}

As far as I know, this is something inexpressible in current Rust directly (i.e. without closures). Closures take their environment through &only reference (currently it is only available to compiler), which forbids taking other references until it goes out of scope. It is like &mut reference but not necessarily tied to mutability. Closure environment capture happens upon its creation, so lifetime of this capture extends to the end of the function, hence you're getting the error.

Why closures should take their environment by unique reference I don't know, though.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top