2

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?

0

1 Answer 1

3

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();
}
Sign up to request clarification or add additional context in comments.

3 Comments

I want to keep that as refs as it's very perf critical code (think the other way has copy cost?). Wonder if borrow_mut has runtime cost, will look into it otherwise use mut on the structs which works too. Thanks a lot!
@Dominic Your intuition about performance of Vec<&SomeStruct> vs. Vec<SomeStruct> is very likely wrong. Write things in the most obvious and maintainable way first, then measure before you start adding &s in places where they cause problems. Yes, by the way, borrow_mut() does have a (small) runtime cost.
@Dominic If it's the size of the struct you're concerned about, consider putting it inside a Box (or Rc/Arc for sharing). Box<Vec<SomeStruct>> has exactly the same size and, once created, is as cheap to use as &Vec<SomeStruct>, but doesn't need to borrow. Either way, though (Box or &), you pay a stiff penalty for adding a layer of indirection, which is likely to cancel out any advantage you get from making the type smaller, unless you do a lot more passing-around than you do dereferencing. Only profiling can say for sure.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.