voxelstack
voxelstack

Reputation: 25

How do I borrow a value inside an `or_else` closure after it's been borrowed for a function call?

I'm making an asset library that's supposed to hold a tree of nodes. A node can be either an Asset or a Directory:

struct Node {
    name: String,
    data: Content,
}

enum Content {
    Directory(Vec<Node>),
    Asset(Rc<dyn Asset>, usize),
}

I have a create_dir_all method that's supposed to take a path and create all directories. The way I want to do that is to iterate over the components of the path, search for the corresponding node, and if it is found, move on to the next, if I get a NotFound error, create the node and then move on.

My first idea was to do this:

let mut current_node = &mut self.root;
for component in path.components() {
    let name = component.as_os_str().to_str().unwrap();

    current_node = current_node.get_child(name).or_else(|error| match error {
        ErrorKind::NotFound => {
            current_node.create_child(Node::directory(String::from(name)))
        }
        error => Err(error),
    })?;
}

I believe I understand why that doesn't work: the closure has to borrow current_node mutably at the time it's created, so the create_child call is a second mutable borrow.

However, I can't seem to find any way around that. Here's another attempt:

let mut current_node = &mut self.root;
for component in path.components() {
    let name = component.as_os_str().to_str().unwrap();

    current_node = match current_node.get_child(name) {
        Ok(child) => Ok(child),
        Err(error) => match error {
            ErrorKind::NotFound => {
                current_node.create_child(Node::directory(String::from(name)))
            }
            error => Err(error),
        },
    }?;
}

Here's my understanding of this one: The lifetime of current_node inside the inner match is the same one that's assigned on the get_child call, so the create_child call will be a second mutable borrow.

While researching this, I found that the lifetime of the result will be tied to the lifetime of current_node. If that's the case, is it impossible to match the error and generate a fallback value? That seems like a very simple thing to do but I cannot figure out what I'm missing.

Upvotes: 0

Views: 215

Answers (1)

voxelstack
voxelstack

Reputation: 25

I had posted this to the Rust forums as well and got a helpful response there: the borrow checker "does not recognize a borrow being continued on one arm of a match but terminated on another."

I went with the suggestion from that response and will look into writing some unsafe code here in the future. It looks like this:

let mut current_node = &mut self.root;
        for component in path.components() {
            let name = component.as_os_str().to_str().unwrap();

            if let Err(error) = current_node.get_child(name) {
                match error {
                    ErrorKind::NotFound => {
                        current_node.create_child(Node::directory(String::from(name)))
                    }
                    error => Err(error),
                }?;
            }

            current_node = current_node.get_child(name)?;
        }

Upvotes: 1

Related Questions