Search code examples
rustimmutabilitytraitsmutable

Assign values to self struct inside implemented function Rust


I recently switched to rust for a project and I am having a hard time with mutable and unmutable objects.

I am having a problem where I need to pass a mutable object to a function inside the implementation function of one of my traits. The error I am getting is the following:

error[E0596]: cannot borrow `self.volume_name` as mutable, as it is behind a `&` reference
   --> src/main.rs:107:40
    |
94  |     fn load_info(&self, name: &str) {
    |                  ----- help: consider changing this to be a mutable reference: `&mut self`
...
107 |         seek_read(&mut opened_file, 3, &mut self.volume_name).unwrap();
    |                                        ^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable

I dont know how to fix this error, since it seems that every fix I try another error pops up. I tried creating a new object and setting the new one to self, but that didnt work either, as self inside this function is a reference, and if I set the addresses the same once I leave the context it will be gone so I get another error! Remember, i am coming from C.

Base code:

pub trait Filesystem {
   fn load_info(&self, name: &str);
}

pub struct Fat16 {
    pub volume_name: [u8; 8],
}

impl Default for Fat16 {
    fn default() -> Fat16 {
        Fat16 {
            volume_name: [0; 8],
        }
    }
}

impl Filesystem for Fat16 {
    fn load_info(&self, name: &str) {

        //I assume the file is a Fat16 volume, the checks are done before

        let mut opened_file = match File::open(&name) {
            Err(why) => panic!("couldn't open {}: {}", name, why),
            Ok(opened_file) => opened_file,
        };

        // ------------------------ VOLUME NAME ------------------------

        // Volume name starts at 3
        seek_read(&mut opened_file, 3, &self.volume_name).unwrap();

        match str::from_utf8(&self.volume_name) {
            Ok(v) => println!("Volume Name: {}", v),
            Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
        };
        
        /*more code*/

Seek read function:

pub fn seek_read(mut reader: impl Read + Seek, offset: u64, buf: &mut [u8]) -> io::Result<()> {
    reader.seek(SeekFrom::Start(offset))?;
    reader.read_exact(buf)?;
    Ok(())
}

Solution

  • self is a & reference, so the data it refers to cannot be borrowed as mutable

    Presumably load_info is going to load information into the Filesystem object. If you want to mutate attributes of self, it needs to be mutable.

    pub trait Filesystem {
       fn load_info(&mut self, name: &str);
                    ^^^^
    }
    
    impl Filesystem for Fat16 {
        fn load_info(&mut self, name: &str) {
                     ^^^^
            let mut opened_file = match File::open(&name) {
                Err(why) => panic!("couldn't open {}: {}", name, why),
                Ok(opened_file) => opened_file,
            };
    
            seek_read(&mut opened_file, 3, &mut self.volume_name).unwrap();
                                           ^^^^
    
            match std::str::from_utf8(&self.volume_name) {
                Ok(v) => println!("Volume Name: {}", v),
                Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
            };
        }
    }