How to call method on struct that has mutable self in Rust

Viewed 185

I am having trouble with the following set up:

#[derive(Debug)]
struct SomeStruct {
    numbers: Vec<u32>,
}

impl SomeStruct {
    fn some_func(&mut self) { // `mut` causes the issue
        self.numbers.push(9); // Contrived but need to call self in here.
        println!("Hello from some func");
    }
}

pub fn main() {
    let struct1 = SomeStruct {
        numbers: vec![1, 2, 3],
    };
    let struct2 = SomeStruct {
        numbers: vec![99, 98, 97],
    };

    let mut vec = Vec::new();
    vec.push(&struct1);
    vec.push(&struct2);

    let first = vec.first_mut().unwrap();

    // cannot borrow `**first` as mutable, as it is behind a `&` reference
    // cannot borrow as mutable rustc(E0596)
    first.some_func();
}

There are many questions about mutable & borrowing but I still can't figure it out. Could someone please explain why this is wrong and how to fix it?

1 Answers

The problem is that vec only has shared references, i.e. &SomeStruct instead of SomeStruct or &mut SomeStruct.

The call to some_func() can't succeed in this setup because it requires &mut self. However, there are ways to work around this:

  1. let vec own SomeStruct, e.g.: vec![struct1, struct2]
  2. Push &mut struct1 and &mut struct2 in the vec
  3. use interior mutability, e.g. through RefCell which would allow you to modify data while holding a shared reference

The interior mutability solution would look something like this:

use std::cell::RefCell;

#[derive(Debug)]
struct SomeStruct {
    numbers: RefCell<Vec<u32>>,
}

impl SomeStruct {
    fn some_func(&self) {
        self.numbers.borrow_mut().push(9); // Contrived but need to call self in here.
        println!("Hello from some func");
    }
}

pub fn main() {
    let struct1 = SomeStruct {
        numbers: RefCell::new(vec![1, 2, 3]),
    };
    let struct2 = SomeStruct {
        numbers: RefCell::new(vec![99, 98, 97]),
    };

    let mut vec = Vec::new();
    vec.push(&struct1);
    vec.push(&struct2);

    let first = vec.first().unwrap();

    // cannot borrow `**first` as mutable, as it is behind a `&` reference
    // cannot borrow as mutable rustc(E0596)
    first.some_func();
}
Related