john1999
john1999

Reputation: 47

How to mutate values of nested structs in Rust?

I'm having trouble figuring out how to mutate nested structs. I have a Folder struct that can hold Files and other Folders inside it. I then want to be able to add new files and folders to those nested folders after creating them, but I keep running into errors.

Code:

type SubFolder = Vec<Box<Folder>>; 
type Files = Vec<String>;

#[derive(Debug)] 
struct Folder { 
    files: Files, 
    sub_folder: SubFolder 
}

impl Folder { 
    pub fn new() -> Self { 
        Self { 
            files: Vec::new(), 
            sub_folder: Vec::new(), 
        } 
    }

    pub fn add_file(&mut self, file: String) {
        self.files.push(file);
    }

    pub fn add_folder(&mut self, dir: Box<Folder>) {
        self.sub_folder.push(dir);
    }
}

fn main() { 
    let mut folder_1 = Folder::new(); 
    let mut folder_2 = Folder::new();

    folder_1.add_folder(Box::new(folder_2));
    folder_2.add_file(String::from("b.txt")); // Error

}

Error Message:

error[E0382]: borrow of moved value: `folder_2`
  --> src/main.rs:32:5
   |
29 |     let mut folder_2 = Folder::new();
   |         ------------ move occurs because `folder_2` has type `Folder`, which does not implement the `Copy` trait
30 |
31 |     folder_1.add_folder(Box::new(folder_2));
   |                                  -------- value moved here
32 |     folder_2.add_file(String::from("b.txt")); // Error
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move

Is it even possible to do this? Or do I need to use unsafe code? Any resources or suggestions would be appreciated.

Upvotes: 1

Views: 791

Answers (1)

Nikolay Zakirov
Nikolay Zakirov

Reputation: 1584

for your methods add_folder and add_file you need mut reference to self. However after folder2 has been moved you can not reference it anymore.

There are a few ways to address it.

  1. You can use "full path".

Like so:

type SubFolder = Vec<Box<Folder>>;
type Files = Vec<String>;
#[derive(Debug)]
struct Folder {
    files: Files,
    sub_folder: SubFolder
}

impl Folder {
    pub fn new() -> Self {
        Self {
            files: Vec::new(),
            sub_folder: Vec::new(),
        }
    }

    pub fn add_file(&mut self, file: String) {
        self.files.push(file);
    }

    pub fn add_folder(&mut self, dir: Box<Folder>) {
        self.sub_folder.push(dir);
    }
}

fn main() {
    let mut folder_1 = Folder::new();
    let mut folder_2 = Folder::new();

    folder_1.add_folder(Box::new(folder_2));
    folder_1.sub_folder[0].add_file(String::from("b.txt")); // Ok
}
  1. Or if you really need to reference folder2 then you have to own it multiple times, so you need Rc for multiple ownership and RefCell for interior mutability as Rc is read only

E.g.:

use std::cell::RefCell;
use std::rc::Rc;

type SubFolder = Vec<Rc<RefCell<Folder>>>;
type Files = Vec<String>;
#[derive(Debug)]
struct Folder {
    files: Files,
    sub_folder: SubFolder
}

impl Folder {
    pub fn new() -> Self {
        Self {
            files: Vec::new(),
            sub_folder: Vec::new()
        }
    }

    pub fn add_file(&mut self, file: String) {
        self.files.push(file);
    }

    pub fn add_folder(&mut self, dir: Rc<RefCell<Folder>>) {
        self.sub_folder.push(dir);
    }
}

fn main() {
    let mut folder_1 = Folder::new();
    let mut folder_2 = Rc::new((RefCell::new(Folder::new())));

    folder_1.add_folder(Rc::clone(folder_2));
    folder_2.borrow_mut().add_file(String::from("b.txt")); // Ok
}

Btw, with Vec you can not address subfolder or file by name. Maybe better use Hashmap?

Upvotes: 1

Related Questions