I have just started learning Rust, and in order to try to get the hang of references, ownership and mutability, I have attempted to make a doubly linked list. It now compiles, and the add function seems to be working as intended.
There is one implementation detail that I am curious about, and in general I'm wondering whether my code violates any good practice. Also, related to this, have I painted myself into any kind of corner if I want to extend this code? (Making add() keep the list ordered, using generics, implementing Iterator, and so on.)
use std::rc::{Rc, Weak};
use std::cell::RefCell;
#[derive(Debug)]
struct Node {
value: i32,
next: RefCell<Option<Rc<Node>>>,
prev: RefCell<Weak<Node>>,
}
impl Node {
pub fn add(&self, this: &Rc<Node>, i: i32) {
if let Some(ref r) = *self.next.borrow() {
r.add(r, i);
return;
};
*self.next.borrow_mut() = Some(Rc::new(Node {
value: i,
next: RefCell::new(None),
prev: RefCell::new(Rc::downgrade(&this)),
}));
}
}
#[derive(Debug)]
struct List {
head: RefCell<Option<Rc<Node>>>,
}
impl List {
pub fn add(&self, i: i32) {
if let Some(ref r) = *self.head.borrow() {
r.add(r, i);
return;
};
*self.head.borrow_mut() = Some(Rc::new(Node {
value: i,
next: RefCell::new(None),
prev: RefCell::new(Weak::new()),
}));
}
}
fn main() {
let list = List {
head: RefCell::new(None),
};
println!("{:?}", list);
list.add(1);
list.add(2);
println!("{:?}", list);
}
The detail I'm curious about is whether the if let parts can be done with a match. Or, more specifically, can it be done without the extra return call. No matter what I tried (both match and if let with else), the borrow() got in the way of the borrow_mut() in the None arm, and I couldn't get the pattern matching to work without the borrow().