Implementing a get_at method in a double linked list

I've wasted so much time on this. Yes, I know that the standard library has a double linked list. I don't plan on actually using this code, it's just for learning.

Here is my code (the relevant function is get_at, everything else works):

use std::cell::{Ref, RefCell};
use std::fmt::{Display, Formatter};
use std::rc::{Rc, Weak};

pub struct Node<T> {
	data: T,
	before: Option<Weak<RefCell<Node<T>>>>,
	after: Option<Rc<RefCell<Node<T>>>>
}
impl<T> Node<T> {
	pub fn new(data: T) -> Self {
		Self {
			data, before: None, after: None
		}
	}
}

pub struct DoubleLinkedList<T> {
	first: Option<Rc<RefCell<Node<T>>>>,
	last: Option<Weak<RefCell<Node<T>>>>
}
impl<T> DoubleLinkedList<T> {
	pub fn new() -> Self {
		Self {
			first: None, last: None
		}
	}
	pub fn append(&mut self, data: T) {
		if self.last.is_some() {
			let last_node_ref = self.last.as_ref().unwrap();
			let mut new_node = Node::new(data);
			new_node.before = Some(last_node_ref.clone());
			let new_node_ref = Rc::new(RefCell::new(new_node));
			last_node_ref.upgrade().unwrap().borrow_mut().after = Some(new_node_ref.clone());
			self.last = Some(Rc::downgrade(&new_node_ref));
		} else {
			let new_node = Node::new(data);
			let rc = Rc::new(RefCell::new(new_node));
			self.first = Some(rc.clone());
			self.last = Some(Rc::downgrade(&rc));
		}
	}
	pub fn get_at(&self, index: usize) -> Option<Rc<RefCell<Node<T>>>> {
		let mut cnt = index;
		let mut node = self.first.as_ref().unwrap();
		while true {
			if cnt != 0 {
				node = node.borrow().after.as_ref().unwrap();
				cnt -= 1;
			} else {
				return Some(node.clone());
			}
		}
		None
	}
}

impl<T: Display> DoubleLinkedList<T> {
	fn fmt_node(node_ref: &Rc<RefCell<Node<T>>>, f: &mut Formatter<'_>) {
		let node = node_ref.borrow();
		write!(f, "node: {{ val: {}, prev: {}, next: {} }}", node.data,
		       match node.before {
			       Some(ref n) => n.upgrade().unwrap().borrow().data.to_string(),
			       None => "(empty)".to_string()
		       }, match node.after {
				Some(ref n) => n.borrow().data.to_string(),
				None => "(empty)".to_string()
			}).expect("y no work");
		match node.after {
			Some(ref next) => Self::fmt_node(next, f),
			None => ()
		}
	}
}

impl<T: Display> Display for DoubleLinkedList<T> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		if self.first.is_some() {
			DoubleLinkedList::fmt_node(self.first.as_ref().unwrap(), f);
			write!(f, "")
		} else {
			write!(f, "(empty list)")
		}
	}
}

I'm getting the error "temporary value dropped while borrowed" and "returns a reference to a value owned by the current function." From my understanding, this should be possible because the Nodes are all owned by DoubleLinkedList, which should stay alive.

Thanks in advance.

Mandatory mention: Introduction - Learning Rust With Entirely Too Many Linked Lists

The error wasn't really about data staying alive or not, it was about trying to return a temporary borrow. Rcs are a type of smart pointer, so instead of working with references to smart pointers, you can just exploit the shared ownership and clone your way out of many lifetime complications:

	pub fn get_at(&self, index: usize) -> Option<Rc<RefCell<Node<T>>>> {
		let mut node = self.first.clone().unwrap();
        for _ in 0..index {
            node = node.clone().borrow().after.clone().unwrap();
        }
        Some(node)
	}

Here node is now an Rc<RefCell<Node<T>>>> instead of a &Rc<RefCell<Node<T>>>>.

Playground.

2 Likes

Remember that RefCell tracks when something is borrowed by giving you a guard and updating a flag when that guard is dropped. That means you need to keep the guard alive any time you want to pass around a reference to a node, but here node is a local variable which will be destroyed when the function returns.

@quinedot's answer works by side-stepping this whole guard business and just bumping the reference count so you can return the Rc by value.

2 Likes

Thanks for the help! (and the book)

I've slightly modified the code to handle none cases, and it works fine.

	pub fn get_idx(&self, index: usize) -> Option<Rc<RefCell<Node<T>>>> {
		let mut node = self.first.clone()?;
		for _ in 0..index {
			node = node.clone().borrow().after.clone()?;
		}
		Some(node)
	}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.