Pregunta

This question is badly named, but basically, consider this simplistic example:

You have a linked list in the form:

struct Node<T> {
    _next: Option<~Node<T>>,
    _data: Option<T>
}

And a 'push' operation that adds elements to the chain:

/** Attach a node as the 'next' node in this chain */
fn push<'a>(&'a mut self, value: T) -> &'a mut ~Node<T> {
    if (self._next.is_none()) {
        self._push_node(~Node::new(value));
    } else {
        let tmp = self._next.take().unwrap();
        let mut next = ~Node::new(value);
        next._push_node(tmp);
        self._push_node(next);
    }
    return self._next.as_mut().unwrap();
}

This works, but it feels like it would be neater as a match() expression instead of an if statement, something like:

/** Attach a node as the 'next' node in this chain */
fn push<'a>(&'a mut self, value: T) -> &'a mut ~Node<T> {
    match self._next {
        None => { self._push_node(~Node::new(value)); },
        Some(ref v)  => { <----------------------------- :(
            let tmp = self._next.take().unwrap(); <------- ???
            let mut next = ~Node::new(value);
            next._push_node(tmp);
            self._push_node(next);
        }
    }
    return self._next.as_mut().unwrap();
}

However, this won't work because of the line indicated above; effectively we're modifying the value of _next, which can't happen because we've borrowed self._next for the scope of the match statement's Some().

Is there a better way of doing this?

Can you somehow declare a match statement purely as a match, so that once you're inside the Some() => { ... } block you are not borrowing the value?

¿Fue útil?

Solución

Because of the Some(ref v), you were borrowing the value. You then didn't use it, so Some(_) would have been fine. But really, you do want to take the value. So what you actually want is to shift the take() outside the match.

Here's the end result:

pub struct Node<T> {
    next: Option<~Node<T>>,
    data: Option<T>
}

/** Attach a node as the 'next' node in this chain */
pub fn push<'a>(&'a mut self, value: T) -> &'a mut ~Node<T> {
    match self.next.take() {
        None => self.push_node(~Node::new(value)),
        Some(v) => {
            let mut next = ~Node::new(value);
            next.push_node(v);
            self.push_node(next);
        }
    }
    match self.next {
        Some(ref mut t) => t,
        None => unreachable!(),
    }
    // Sure, you could replace those four lines with self.next.as_mut().unwrap(),
    // but I have a slight leaning towards highlighting the unreachable nature of
    // the None branch. It makes it more explicit.
    // Others will doubtless disagree with me on that point.
}

You could also have gone for Some(ref mut v) and manipulated that &mut ~Node<T> value directly, but that would presumably require altering how push_node works. (You haven't shown the code for it so I can only guess what it does.)

A few other stylistic changes I've made:

  • four spaces, not two;
  • don't prefix things with underscore (Rust has sufficient privacy control, no need to use underscores);
  • no parentheses around the if expression (well, now that's been replaced with match, so it doesn't matter);
  • if there is but one statement in a match branch, there's no need for the curly braces around it—just use a comma after it and that's it,
  • return x; can be written as just x when it's at the end of a function.
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top