ParkCheolu
ParkCheolu

Reputation: 1275

How can I consume data type of Rc<RefCell<T>> in struct?

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 type std::cell::RefCell<ds::dll::DbNode<T>>, which does not implement the Copy 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

Answers (1)

kmdreko
kmdreko

Reputation: 60752

You were close. Use Rc::try_unwrap():

Returns the inner value, if the Rc has exactly one strong reference. Otherwise, an Err is returned with the same Rc 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

Related Questions