Search code examples
rustborrow-checker

Cannot borrow *self as immutable, but I cannot find a way around


So I have the above error in line 17, that I understand, but don't find a good way around:

struct Node {
    node_type: NodeType
}

enum NodeType {
    Inner([Box<Node>; 16]),
    Leaf(Vec<usize>)
}

impl Node {
    fn foo(&mut self) {
        match &mut self.node_type {
            NodeType::Leaf(_content) => {
                // Mutate content
            },
            NodeType::Inner(children) => {
                let index = self.compute_index();
                let child = &mut children[index];
                child.foo();
            }
        }
    }

    fn compute_index(&self) -> usize {
        // compute something
        0
    }
}

Playground

I need self to be mutable, to mutate its content in the Leaf case. I could copy the code from compute_index() into my foo() function, but it would make the foo() function unnecessarily long and the code inside the compute_index() seems like a separate semantical unit on its own.

I could also move the call to compute_index() above the match statement, but I don't need to execute it in all match arms, so this would cause unnecessary computations in that case and doesn't seem right either.

I thought about this for quite some time, but cannot figure out a way to get rid of this error without making my code bad. Does anyone have ideas on how to get rid of the error without making the code bad?


Solution

  • Can you shuffle the things around to compute the index before acquiring a mutable borrow?

    impl Node {
        fn foo(&mut self) {
            if let NodeType::Leaf(_content) = &mut self.node_type {
                // Mutate content
                _content.push(42);
            } else {
                let index = self.compute_index();
                if let NodeType::Inner(children) = &mut self.node_type {
                    let child = &mut children[index];
                    child.foo();
                }
            }
        }
    
        fn compute_index(&self) -> usize {
            // compute something
            0
        }
    }