3
pub struct ForesterViewModel {
    m_tree_lines: Arc<Mutex<Vec<TreeLine>>>,
}

impl ForesterViewModel {
    pub fn new() -> ForesterViewModel {
        ForesterViewModel {
            m_tree_lines: Arc::new(Mutex::new(vec![])),
        }
    }

    pub fn get_the_forest(&mut self) -> &mut Vec<TreeLine> {
        ???????????????????????????????
    }
}

I need help writing the get_the_forest function. I've tried many various things but they all return compilation errors. I need to return a mutable reference to Vec<TreeLine> which is wrapped behind an Arc and a Mutex in self.m_tree_lines.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Refael Sheinker
  • 713
  • 7
  • 20
  • 3
    You can't really do this. The whole point of `Mutex` is that you lock the data inside and prevent it from being accessed until the reference to it is dropped. This is accomplished by forcing the data to be access through a [`MutexGuard`](https://doc.rust-lang.org/std/sync/struct.MutexGuard.html). – Aplet123 Mar 20 '21 at 21:05
  • 1
    why don't you return the mutex guard ? – Stargateur Mar 20 '21 at 21:06
  • 1
    https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=12fd19d7ab48f2fb002fc3d936bf1f4c – Stargateur Mar 20 '21 at 21:14
  • @Aplet123 I see you point and I understand. Followup question: if I change the return value to be immutable, `&Vec`, would it be possible then? – Refael Sheinker Mar 20 '21 at 21:39
  • 3
    Does this answer your question? [Mutable borrow to object inside Mutex - how to refactor?](https://stackoverflow.com/questions/59069382/mutable-borrow-to-object-inside-mutex-how-to-refactor) or perhaps [How to return a reference to a sub-value of a value that is under a mutex?](https://stackoverflow.com/questions/40095383/how-to-return-a-reference-to-a-sub-value-of-a-value-that-is-under-a-mutex) – kmdreko Mar 21 '21 at 03:21
  • 3
    @RefaelSheinker the reference being immutable doesn't help. *No* references can refer to the inner value of the Mutex without keeping the guard, that's now it knows its safe to be unlocked elsewhere. – kmdreko Mar 21 '21 at 03:24
  • Thank you all who helped. I now understand. – Refael Sheinker Mar 23 '21 at 21:03

1 Answers1

2

There is no way of doing this.

You create a concrete MutexGuard object that releases the mutex when it dropped when you call lock; you cannot move a reference out of the scope that contains the guard:

pub fn as_mut(&mut self) -> &Whatever {
  let mut guard = self.data.lock().unwrap();
  Ok(guard.deref())
  drop(guard) // <--- implicitly added here, which would invalidate the ref
}

You also cannot return both the mutex guard and a reference, for more complex reasons (basically rust cannot express that), for the same reason it cannot have a reference and an object in a single structure; see the discussion on Why can't I store a value and a reference to that value in the same struct?

...so basically your best bet is one of two things:

/// Return the mutex guard itself
pub fn get_the_forest(&mut self) -> Result<MutexGuard<Vec<TreeLine>>, TreeLockError> {
    Ok(self.m_tree_lines.lock()?)
}
/// Pass a function in, which patches the mutable internal value
pub fn patch_forest(&mut self, patch: impl Fn(&mut Vec<TreeLine>)) -> Result<(), TreeLockError>{
    let mut guard = self.m_tree_lines.lock()?;
    patch(&mut guard); // <-- patch happens while guard is still alive
    Ok(())
}

Full code:

use std::sync::{Arc, Mutex, MutexGuard};
use std::sync::PoisonError;
use std::error::Error;
use std::fmt;
use std::fmt::Formatter;
use std::ops::Deref;

#[derive(Debug, Copy, Clone)]
pub enum TreeLockError {
    FailedToLock
}

impl Error for TreeLockError {}

impl fmt::Display for TreeLockError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

impl<T> From<PoisonError<T>> for TreeLockError {
    fn from(_: PoisonError<T>) -> Self {
        TreeLockError::FailedToLock
    }
}

// ---

#[derive(Debug)]
pub struct TreeLine {
    pub value: &'static str
}

pub struct ForesterViewModel {
    m_tree_lines: Arc<Mutex<Vec<TreeLine>>>,
}

impl ForesterViewModel {
    pub fn new() -> ForesterViewModel {
        ForesterViewModel {
            m_tree_lines: Arc::new(Mutex::new(vec![])),
        }
    }

    pub fn get_the_forest(&mut self) -> Result<MutexGuard<Vec<TreeLine>>, TreeLockError> {
        Ok(self.m_tree_lines.lock()?)
    }
    
    pub fn patch_forest(&mut self, patch: impl Fn(&mut Vec<TreeLine>)) -> Result<(), TreeLockError>{
        let mut guard = self.m_tree_lines.lock()?;
        patch(&mut guard);
        Ok(())
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let mut vm = ForesterViewModel::new();
    
    {
        let mut trees = vm.get_the_forest()?;
        trees.push(TreeLine{ value: "one"});
        trees.push(TreeLine{ value: "two"});
    } // <--- Drop the mutable reference here so you can get it again later
    
    // Patch
    vm.patch_forest(|trees| {
        trees.push(TreeLine{ value: "three"}); 
    });
    
    // ...
    let trees = vm.get_the_forest()?;
    println!("{:?}", trees.deref());
    
    Ok(())
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Doug
  • 32,844
  • 38
  • 166
  • 222