Toma
Toma

Reputation: 2936

Rust - return a mutable reference to an element in Vec owned by a struct

Here is my struct:

pub(crate) struct Graph {
    outbound: HashMap<isize, Vec<isize>>,
    inbound: HashMap<isize, Vec<isize>>,
    edges: Vec<Edge>,
}

and what I tried:

fn get_edge_mut(&mut self, from_id: isize, to_id: isize) -> Result<&mut Edge, GraphError> {
    let mut edges = self
        .edges
        .iter_mut()
        .filter(|edge| edge.get_from() == from_id && edge.get_to() == to_id)
        .collect::<Vec<_>>();
    match edges.len() {
        0 => Err(GraphError(format!(
            "edge from {} to {} does not exist",
            from_id, to_id
        ))),
        1 => Ok(edges[0]),
        _ => panic!(
            "wrong graph structure - edge from {} to {} exists multiple times",
            from_id, to_id
        ),
    }
}

The problem is here: Ok(edges[0])

It fails with cannot return value referencing local variable "edges"

I understand that edges is owned by the current function and I cannot return a reference to it because it goes out of scope, but what would be a better way to return a mutable reference to an element in the "edges" Vec (of the struct) based on some condition?

Upvotes: 1

Views: 737

Answers (1)

EtomicBomb
EtomicBomb

Reputation: 36

I would suggest using:

    fn get_edge_mut(&mut self, from_id: isize, to_id: isize) -> Result<&mut Edge, GraphError> {
        self.edges.iter_mut()
            .find(|edge| edge.get_from() == from_id && edge.get_to() == to_id)
            .ok_or(GraphError(format!("edge from {} to {} does not exist", from_id, to_id)))
    }

It skips your "wrong graph structure check", but if it's needed, you could go with

fn get_edge_mut(&mut self, from_id: isize, to_id: isize) -> Result<&mut Edge, GraphError> {
    let mut edges_matching = self.edges.iter_mut()
        .filter(|edge| edge.get_from() == from_id && edge.get_to() == to_id);

    let edge = edges_matching.next()
        .ok_or(GraphError(format!("edge from {} to {} does not exist", from_id, to_id)));

    assert_eq!(edges_matching.count(), 0,
               "wrong graph structure - edge from {} to {} exists multiple times",
               from_id, to_id);

    edge
}

It seems like the issue might stem from edges[0] ultimately calling SliceIndex::index, which basically returns an Option<&&mut Edge>, where that first & is actually a reference to the current function.

trendcl is right, edges.pop() works too.

Upvotes: 2

Related Questions