Borrowed but no clue where or why or how to fix it

I've turned everything into String and it's still complaining. No clue why.

    let mut nodes: HashMap<&String, NodeIndex> = HashMap::new();
    let mut graph = Graph::<String, u32>::new();

    for line in input.split('\n') {
        println!("Record: {}", line);

        let (container, colors) = parse_line(line);

        let node = match nodes.get(&container) {
            Some(n) => *n,
            None => {
                let ni = graph.add_node(container);
                nodes.insert(&container, ni);
                ni
            }
        };

        for col in colors {
            let contained_node = match nodes.get(&container) {
                Some(n) => *n,
                None => {
                    let ni = graph.add_node(col);
                    nodes.insert(&container, ni);
                    ni
                }
            };

            graph.add_edge(node, contained_node, 0);
        }
    }

    println!("Generated graph: {:?}", graph);

This is parse_line:

fn parse_line(line: &str) -> (String, Vec<String>) {}

borrowed value does not live long enough
container dropped here while still borrowed

I don't know who's borrowing it and why it's not living long enough.

I'm passing the self-owned strings to Graph. Shouldn't that be enough?

How does this work? I should just use String everywhere and never bother with &str anymore?

Code with all changes necessary to get to the borrowck error:

use petgraph::prelude::*;
use std::collections::HashMap;

fn make_graph(input: &str) {
    let mut nodes: HashMap<&String, NodeIndex> = HashMap::new();
    let mut graph = Graph::<String, u32>::new();

    for line in input.split('\n') {
        println!("Record: {}", line);

        let (container, colors) = parse_line(line);

        let node = match nodes.get(&container) {
            Some(n) => *n,
            None => {
                let ni = graph.add_node(container);
                nodes.insert(&container, ni);
                ni
            }
        };

        for col in colors {
            let contained_node = match nodes.get(&container) {
                Some(n) => *n,
                None => {
                    let ni = graph.add_node(col);
                    nodes.insert(&container, ni);
                    ni
                }
            };

            graph.add_edge(node, contained_node, 0);
        }
    }

    println!("Generated graph: {:?}", graph);
}

fn parse_line(line: &str) -> (String, Vec<String>) {todo!();}

The borrowck errors:

error[E0597]: `container` does not live long enough
  --> src/lib.rs:17:30
   |
13 |         let node = match nodes.get(&container) {
   |                          ----- borrow later used here
...
17 |                 nodes.insert(&container, ni);
   |                              ^^^^^^^^^^ borrowed value does not live long enough
...
34 |     }
   |     - `container` dropped here while still borrowed

error[E0382]: borrow of moved value: `container`
  --> src/lib.rs:17:30
   |
11 |         let (container, colors) = parse_line(line);
   |              --------- move occurs because `container` has type `String`, which does not implement the `Copy` trait
...
16 |                 let ni = graph.add_node(container);
   |                                         --------- value moved here
17 |                 nodes.insert(&container, ni);
   |                              ^^^^^^^^^^ value borrowed here after move

error: aborting due to 2 previous errors; 1 warning emitted

The second error says that you first moved container into graph and then tried to take a reference to the old location and store it in nodes. This won't work. The first error says that you tried to store a reference to the container value, which only lives inside the first for loop and try to store it in a value that outlives the first for loop.

But how do I fix it? Is my approach fundamentally wrong here?

Also:

borrow later used here

What's later about it if the line of code is the first?

You are trying to borrow a variable after giving the ownership to another function. You can't use a variable after you move it. Try reading the part about ownership in the book.

I'm going to reread the chapter in the O'Reilly book and then read this one too but they weren't too clear to begin with.

I fixed it by not trying to put the same string in the Graph (which seems to be useless anyway) and moving ownership to the HashMap:

    let mut nodes: HashMap<String, NodeIndex> = HashMap::new();
    let mut graph = Graph::<u32, u32>::new();

    for line in input.split('\n') {
        println!("Record: {}", line);

        let (container, colors) = parse_line(line);

        let node = match nodes.get(container.as_str()) {
            Some(n) => *n,
            None => {
                let ni = graph.add_node(0);
                nodes.insert(container, ni);
                ni
            }
        };

        for col in colors {
            let contained_node = match nodes.get(col.as_str()) {
                Some(n) => *n,
                None => {
                    let ni = graph.add_node(0);
                    nodes.insert(col, ni);
                    ni
                }
            };

            graph.add_edge(node, contained_node, 0);
        }
    }

    println!("Generated graph: {:?}", graph);

Is that the right way to go about this?

Just to clarify this part - because it was in a loop (imagine the loop unrolled).

I might just be over-explaining things, but as bjorn3 says:

Your initial HashMap had a reference &String in it

You would then later insert a reference to &container:

But for the reference to remain valid the original version of it must be owned somewhere. The variable container is created and goes out of scope in each loop. Your switch to .as_str() is solving that by using a function which returns a new owned value.

It sounds like you might want the string to be owned by just one of the objects you're inserting it into. I'd think about reordering your code to insert the owned version first, and then make the reference to that owned version in the nodes HashMaps instead of to &container.

1 Like