Cloned() iterator still implies a borrow?


#1

I’m trying to search through an object - a graph - for a particular element, then either update it’s value (if it exists) or add it (if it doesn’t). But the Rust compiler tells me that I can’t because the call to the initial iterator (“edges()”) is an immutable borrow of the graph object, so I can’t mutate it later - fair enough.

After search around online, I see that the general solution is to clone() or cloned() the iterator, but that’s not working for me. What am I missing here?

let out_edge = graph.edges(prev_node)
                                 .cloned()
                                 .find( |&e| function_body_here );
match out_edge {
    Some(e) => {
        *self.graph.edge_weight_mut(e.id()).unwrap() += 1;
    }
    None    => {
        let new_node = self.graph.add_node(xchar);
        self.graph.add_edge(prev_node, new_node, 1);
        prev_node = new_node;
    }
}

#2

Can you give us the exact error your getting? Note that an object you own can still contain references.

In stead of cloning all the values you could also just clone the result of find or even just the id value.


#3

Without the cloned() I get:

error[E0502]: cannot borrow `self.graph` as mutable because it is also borrowed as immutable

–> src/main.rs:210:29
|
201 | let out_edge = self.graph.edges(prev_node)
| ---------- immutable borrow occurs here

210 | self.graph.add_edge(prev_node, new_node, 1);
| ^^^^^^^^^^ mutable borrow occurs here

217 | },
| - immutable borrow ends here

With the cloned() I get:

   Compiling poa_test v0.1.0 (file:///Users/bbowman/Rust/biofx/poa)
error[E0271]: type mismatch resolving `<petgraph::graph::Edges<'_, u16, petgraph::Directed> as std::iter::Iterator>::Item == &_`
   --> src/main.rs:202:47
    |
202 |                                              .cloned()
    |                                               ^^^^^^ expected struct `petgraph::graph::EdgeReference`, found reference
    |
    = note: expected type `petgraph::graph::EdgeReference<'_, u16>`
               found type `&_`

EdgeReference here being, I think, just a convenience wrapper around a <&Edge> type.
If I’m getting a reference instead of a value, I can understand why it’s treated as a borrow. And cloned() is supposed to convert an Iterator of <&T> to an Iterator of , right?

Perhaps it’s an issue with the petgraph library’s implementation of “cloned()”?


#4

The element isn’t borrowed, but the container you’re iterating is borrowed. Rust prevents it from being mutated while being iterated (otherwise the iterator could lose its position).


#5

The edges() function returns an iterator over EdgeReference. Note that this struct contains a lifetime coupled to the Graph. As long as the EdgeRefrence is alive you can’t mutate the graph. This results in the first error. The cloned is not working as your not iterating over a reference.

Try:
let out_edge = graph.edges(prev_node).find( |&e| function_body_here ).map(|e| e.id());

That way you don’t keep the EdgeRefrence alive and thus you can use the Graph.