Reputation: 1275
I'm trying to implement an linked list for learning purposes. std::cell::RefCell and stc::rc::{Rc, Weak} are mainly used to store data into the list instance. Now I'm implementing fn pop, which consumes and returns a value in the first location in the list, but I don't know how to consume a value wrapped with Rc and RefCell.
Here is my code:
use std::cell::RefCell;
use std::rc::{Rc, Weak};
#[derive(Debug)]
pub struct DbNode<T> {
data: T,
next: Option<Rc<RefCell<DbNode<T>>>>,
prev: Option<Weak<RefCell<DbNode<T>>>>,
}
#[derive(Debug)]
pub struct DbList<T> {
first: Option<Rc<RefCell<DbNode<T>>>>,
last: Option<Weak<RefCell<DbNode<T>>>>,
}
pub fn push_front(&mut self, data: T) {
match self.first.take() {
Some(e) => {
let new_front = Rc::new(RefCell::new(DbNode {
data,
next: Some(e.clone()),
prev: None,
}));
let mut me = e.borrow_mut();
me.prev = Some(Rc::downgrade(&new_front));
self.first = Some(new_front);
},
None => {
let new_data = Rc::new(RefCell::new(DbNode {
data,
next: None,
prev: None,
}));
self.last = Some(Rc::downgrade(&new_data));
self.first = Some(new_data);
},
}
}
pub fn push_back(&mut self, data: T) {
match self.last.take() {
Some(l) => {
let new_back = Rc::new(RefCell::new(DbNode {
data,
next: None,
prev: Some(l.clone()),
}));
let st = Weak::upgrade(&l).unwrap();
let mut ml = st.borrow_mut();
self.last = Some(Rc::downgrade(&new_back));
ml.next = Some(new_back);
},
None => {
let new_data = Rc::new(RefCell::new(DbNode {
data,
next: None,
prev: None,
}));
self.last = Some(Rc::downgrade(&new_data));
self.first = Some(new_data);
},
}
}
pub fn pop(&mut self) -> Option<T> {
match self.first.take() {
Some(f) => {
// How can I??
// let result = Some(f.into_inner().data);
// result
},
None => None,
}
}
What I want to achieve is to return the inner 'data' value in the struct DbNode located in the 'first' in struct DbList and set None to the 'first' in which the data to be consumed located if 'next' is None, else set 'next' to 'first'. At first, I tried to consume the inner value with using Rc::downcast, but the type of 'f' in matching block was 'RefCell', not 'Rc', then I tried to use RefCell::into_inner(), but compiler says:
cannot move out of an
Rc
move occurs because value has typestd::cell::RefCell<ds::dll::DbNode<T>>
, which does not implement theCopy
trait
I totally understand what this means, but I don't know what I should do. What is the correct way to do it??
Upvotes: 3
Views: 1912
Reputation: 60752
You were close. Use Rc::try_unwrap()
:
Returns the inner value, if the
Rc
has exactly one strong reference. Otherwise, anErr
is returned with the sameRc
that was passed in. This will succeed even if there are outstanding weak references.
pub fn pop(&mut self) -> Option<T> {
match self.first.take() {
Some(f) => {
match Rc::try_unwrap(f) {
Ok(refcell) => {
// you can do refcell.into_inner here
Some(refcell.into_inner().data)
},
Err(_) => {
// another Rc still exists so you cannot take ownership of the value
None
}
}
},
None => None,
}
}
Upvotes: 7