0
votes

I've never understood why I have received the Rust error "cannot move out of borrowed content".

use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::vec::Vec;

pub struct user_type {
    pub name: String,
    pub ilist: Vec<i32>,
    pub user_type_list: VecDeque<Option<Rc<RefCell<user_type>>>>,
    pub parent: Option<Rc<RefCell<user_type>>>,
}

impl user_type {
    pub fn new(name: String) -> Self {
        user_type {
            name: name.clone(),
            ilist: Vec::new(),
            user_type_list: VecDeque::new(),
            parent: Option::None,
        }
    }

    pub fn to_string(&self) -> String {
        let mut result: String = String::new();

        result += "name is ";
        result += &self.name;

        let n = self.user_type_list.len();

        for iter in &self.user_type_list {
            match iter {
                Some(ref x) => {
                    let temp = x.into_inner();
                    let temp2 = temp.to_string();
                    result += &temp2[..];
                }
                None => panic!("to_string"),
            }
            result += "\n";
        }

        result
    }
}

The full error message is:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:34:32
   |
34 |                     let temp = x.into_inner();
   |                                ^ cannot move out of borrowed content

What is the origin of this kind of error?

1
Idiomatic Rust uses snake_case for variables, methods, macros, and fields; UpperCamelCase for types; and SCREAMING_SNAKE_CASE for statics and constants. Use UserType instead, please.Shepmaster

1 Answers

0
votes

Look carefully at this code:

for iter in &self.user_type_list {
    match iter {
        Some(ref x) => {
            let temp = x.into_inner();
            let temp2 = temp.to_string();
            result += &temp2[..];
        }
        None => panic!("to_string"),
    }
    result += "\n";
}

Here, you are iterating &self.user_type_list so the type of iter is actually a reference to the contained value: &Option<Rc<RefCell<user_type>>>. That is nice, because you do not want to take ownership of the container or its values.

Then you match iter to Some(ref x). Older compiler versions would fail because you are matching a reference to a non-reference, but new compilers will do as if you are matching a Option<&T> instead of a &Option<T>, if needed. That is handy, and means that you can write just Some(x) => and x will be of type &Rc<RefCell<user_type>> instead of &&Rc<..> (not that it really matters, automatic dereferencing will make those equivalent).

Now you are calling x.into_inner() with a &Rc<RefCell<..>> and that will never work. It looks like you want to get the RefCell into temp that is not needed, Rc implements Deref so you get that for free. Instead the compiler thinks you are calling RefCell::into_inner(self) -> T, but this function consumes the self to get to the contained value. And you do not own it, you just borrowed it. That is what the error message means: you are trying to consume (move out) and object you do not own (borrowd).

What you really want is just to borrow the user_type enough to call to_string():

Some(x) => { 
    let temp = x.borrow().to_string();
    result += &temp;
}