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?
source to share
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 bymatch
, 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 soonx
as it is at the end of the function.
source to share