1

I'm trying to create a parameter structure that will possibly be shared between threads. It has a member called layer_storage which some members will need to mutate. I tried the following code, but am getting an error saying that the cloned Arc does not live long enough. This same member worked fine before the addition of Arc<Mutex<>>.

use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct Params {
    pub optional: Vec<f32>,
}

pub struct ParamManager {
    layer_storage: Arc<Mutex<Vec<Params>>>,
}

impl Default for ParamManager {
    fn default() -> ParamManager {
        ParamManager {
            layer_storage: Arc::new(Mutex::new(vec![Params { optional: vec![1.0f32, 2.0f32] },
                                                    Params { optional: vec![3.0f32, 4.0f32] }])),
        }
    }
}

impl ParamManager {
    pub fn get_mut_params(&mut self, layer_index: usize) -> &mut Params {
        let layers_arc = self.layer_storage.clone();
        let layers = layers_arc.get_mut().unwrap();
        // tried this initially:
        // let layers = self.layer_storage.clone().get_mut().unwrap();
        assert!(layers.len() - 1 >= layer_index);
        &mut layers[layer_index]
    }
}

fn main() {
    let mut bla = ParamManager::default();
    let i = bla.get_mut_params(0);
}

(Playground)

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
bge0
  • 901
  • 2
  • 10
  • 25
  • Did you try to assign the clone to a variable, then do the rest in a separate statement? – ZeissS May 22 '16 at 21:58
  • Yes, that is what I did: self.layer_storage.clone() and then work with that as a variable. – bge0 May 22 '16 at 21:59

2 Answers2

2

As @Shepmaster says you can't just return a reference to something inside the Arc<Mutex<T>> from the get_mut_params; this is one of the guarantees you get from them!

One solution which works in may cases is to turn the function inside out; rather than returning a mutable reference, take a closure which is given the mutable reference:

impl ParamManager {
    pub fn with_mut_params<F>(&mut self, layer_index: usize, mut f: F)
                     where F: FnMut(&mut Params) {
        let layers_arc = self.layer_storage.clone();
        let layers = layers_arc.lock().unwrap();
        f(&mut layers[layer_index]);
    }
}

fn main() {
    let mut bla = ParamManager::default();

    // Context used by the closure
    let some_var: i32 = 7;
    let other_var: mut MyContext = do_something();

    bla.with_mut_params(0, |i| {
       // do stuff with i
       ...
       // A closure has access to surrounding variables
       other_var.foo(i, some_var);
    });

}
Chris Emerson
  • 13,041
  • 3
  • 44
  • 66
  • Thank you, this is a pretty elegant way to handle the problem. Only issue I see here is the generalizability: i.e. the closure only accepts one parameter. If some context were needed this might not work as well (especially since rust doesn't have overloading). – bge0 May 23 '16 at 15:23
  • Being a closure rather than just a function pointer means it can include context. Can you give an example that shows what might not be possible? – Chris Emerson May 23 '16 at 16:15
  • Since you have `where F: FnMut(&mut Params)` the only type of acceptable closure would be one that takes in just `&mut Params`, correct? Supposing a function would need a few, eg: `where F: FnMut(&mut Params)` **and** where `F: FnMut(&mut Params, &MyContext, i32)` it would need two different overloaded ops for this which is not possible (without a bunch of `Optional<>` 's which is a little ugly – bge0 May 23 '16 at 22:01
  • I'll edit my answer to show how a closure solves that, as a comment is too short. – Chris Emerson May 23 '16 at 22:05
  • I think I realize! Did a quick read from the rust book. It takes into account local scope as well! – bge0 May 23 '16 at 22:07
1

I think the compiler is right in your case.

There are two obvious errors here:

  1. The layers_arc value lives just until the block end, and then it's destructor will decrement the reference counter and maybe drop the whole value. Or it could be dropped in other thread in any time. So it is illegal to return a pointer to it's contents.

  2. Mutex::get_mut method requires &mut self, while it is impossible to get it directly from Arc.

So you have to refactor your code somehow. For example, you could guard each separate item in vector with Arc<Mutex> and return them by value using .clone().

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
swizard
  • 2,551
  • 1
  • 18
  • 26
  • Thank you @swizard. This is a valid answer as well. Just decided to go with the one below for cleanliness. – bge0 May 23 '16 at 22:09