I am learning about link list & thought to write a simple one.
struct ListNode {
val: i32,
next: Option<Box<ListNode>>,
}
with a impl
block
impl ListNode {
fn new(val: i32) -> Self {
ListNode { next: None, val }
}
// Issue with insert method.
fn insert(&mut self, val: i32) -> &mut Self {
// Match causing issue because it takes mut ref.
match &mut self.next {
Some(node) => node.insert(val),
None => {
// Cannot update value because only one mut ref allowed.
self.next = Some(Box::new(ListNode::new(val)));
self
}
}
}
}
If I replace mut
ref with just self in insert() it works fine but at every insert call transferring ownership doesn't feel logical. Is there any way to insert new item in link list while only taking node's ref?
This is a niche case where you would want to bypass match ergonomics so the None
case doesn't hold an exclusive reference. You can do that by matching on the value directly (not a mutable reference) and binding to a variable via mut ref
like so:
fn insert(&mut self, val: i32) -> &mut Self {
match self.next {
Some(ref mut node) => node.insert(val),
None => {
self.next = Some(Box::new(ListNode::new(val)));
self
}
}
}
For some background: "match ergonomics" is the mechanism that allows matching on references to seamlessly match on the referent's structure, and thus cause the bindings to be references into that structure. However, the problem here is that the mutable reference is created in the match scrutinee and is held for the whole match, even when the None
case doesn't need to preserve any reference. Using the older ref
syntax makes it so that only the Some
case needs to create and hold a mutable reference while the None
case does not, leaving self
unbound.