Search code examples
rustmutex

Rustic way to mutate struct using its attributes outside itself


I want to mutate Outer struct using itself outside its implementation and without coping its attributes. How to implement it in Rust correctly? Is there a rustic way to have mutable and immutable references in the same time?

Here the code example (maybe this is far-fetched, but it illustrates the problem I face very good):

use std::sync::Mutex;

#[derive(Debug)]
struct Inner {
    n: i64,
    factor: i64,
}

#[derive(Debug)]
struct Max {
    v: i64
}

#[derive(Debug)]
struct Outer {
    inners: Vec<Inner>,
    max: Max,
}

fn main() {
    let mutex_s = Mutex::new(Outer {
        inners: vec![
            Inner { n: 1, factor: 2 },
            Inner { n: 2, factor: 2 },
            Inner { n: 3, factor: 3 },
            Inner { n: 4, factor: 3 },
        ],
        max: Max { v: 5 },
    });

    let mut s = mutex_s.lock().unwrap();
    
    s.inners.retain(|i| i.n * i.factor < s.max.v);

    println!("{:?}", s);
}

Here I get the following error: cannot borrow 's' as immutable because it is also borrowed as mutable.

I guess here I need interior mutability, but I'm not sure how to implement it correctly, optimally and effectively.


Solution

  • If you need concurrent access to inners and max you need them to be in different Mutexes, the Mutex should be around the current type of inners, (in your example max is only accessed immutably so it doesn't need a Mutex) not around the whole Outer struct. In other words: for every field that you need to access simultaneously you need a different Mutex, here that's just the inners member:

    #[derive(Debug)]
    struct Outer {
        inners: Mutex<Vec<Inner>>,
        max: Max,
    }
    
    fn main() {
        let s = Outer {
            inners: Mutex::new(vec![
                Inner { n: 1, factor: 2 },
                Inner { n: 2, factor: 2 },
                Inner { n: 3, factor: 3 },
                Inner { n: 4, factor: 3 },
            ]),
            max: Max { v: 5 },
        };
        
        s.inners.lock().unwrap().retain(|i| i.n * i.factor < s.max.v);
    
        println!("{:?}", s);
    }
    

    Alternatively, if the fields are visible to you (they are pub or Outer is a local type) you can split the borrow:

    fn main() {
        let mutex_s = Mutex::new(Outer {
            inners: vec![
                Inner { n: 1, factor: 2 },
                Inner { n: 2, factor: 2 },
                Inner { n: 3, factor: 3 },
                Inner { n: 4, factor: 3 },
            ],
            max: Max { v: 5 },
        });
    
        let mut s = mutex_s.lock().unwrap();
    
        // splitting the borrow into `inners` and `max` so only `max` is captured in the closure.
        let Outer { inners, max } = &mut *s;
    
        // or for more control over the mutability:
        let Outer {
            ref mut inners,
            ref max,
        } = *s;
    
        inners.retain(|i| i.n * i.factor < max.v);
    
        println!("{:?}", s);
    }