Is there a way to use match () in rust when the selector changes?

This question is poorly named, but mainly consider this simplified example:

You have a linked list in the form:

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

      

And the "push" operation, which adds items 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 looks like it would lean like a match () expression instead of an if statement, like this:

/** 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 above line; effectively we are changing the value of _next, which cannot happen because we borrowed self._next for the scope of the Some () expression.

Is there a better way to do this?

Can you somehow declare the result of the match as just a match, so that once you hit the Some () => {...} block, you don't borrow the value?

+1


source to share


1 answer


Because of the Some(ref v)

cost you borrowed. You didn't use it back then, so Some(_)

it would be ok. But in fact, you really want to take advantage of this value. So you really want to slide take()

out of 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 go for Some(ref mut v)

and manipulate this value directly &mut ~Node<T>

, but that would probably require changing how it works push_node

. (You haven't provided any code for this, so I can only guess what it does.)

Several other stylistic changes I made:

  • four spaces, not two;
  • not prefix things with underscores (Rust has enough privacy controls, no need to use underscores);
  • no parentheses around the expression if

    (well, now that's replaced by match

    , so it doesn't matter);
  • if there is only one statement in the match branch, there is no need for curly braces around it - just use a comma after it and that it,
  • return x;

    can be written as soon x

    as it is at the end of the function.
+5


source







All Articles