Home > OS >  Can I implement Index/IndexMut for a type that has locked data?
Can I implement Index/IndexMut for a type that has locked data?

Time:01-27

I've got a struct that contains some locked data. The real world is complex, but here's a minimal example (or as minimal as I can make it):

use std::fmt::Display;
use std::ops::{Index, IndexMut};
use std::sync::Mutex;

struct LockedVector<T> {
    stuff: Mutex<Vec<T>>,
}

impl<T> LockedVector<T> {
    pub fn new(v: Vec<T>) -> Self {
        LockedVector {
            stuff: Mutex::new(v),
        }
    }
}

impl<T> Index<usize> for LockedVector<T> {
    type Output = T;
    fn index(&self, index: usize) -> &Self::Output {
        todo!()
    }
}

impl<T> IndexMut<usize> for LockedVector<T> {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        let thing = self.stuff.get_mut().unwrap();
        &mut thing[index]
    }
}

impl<T: Display> Display for LockedVector<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let strings: Vec<String> = self
            .stuff
            .lock()
            .unwrap()
            .iter()
            .map(|s| format!("{}", s))
            .collect();
        write!(f, "{}", strings.join(", "))
    }
}

fn main() {
    let mut my_stuff = LockedVector::new(vec![0, 1, 2, 3, 4]);
    println!("initially: {}", my_stuff);
    my_stuff[2] = 5;
    println!("then: {}", my_stuff);
    let a_mut_var: &mut usize = &mut my_stuff[3];
    *a_mut_var = 54;
    println!("Still working: {}", my_stuff);
}

What I'm trying to do here is implement the Index and IndexMut traits on a struct, where the data being indexed is behind a Mutex lock. My very fuzzy reasoning for why this should be possible is that the result of locking a mutex is sort-of like a reference, and it seems like you could map a reference onto another reference, or somehow make a sort of reference that wraps the entire lock but only de-references the specific index.

My much less fuzzy reasoning is that the code above compiles and runs (note the todo!) - I'm able to get back mutable references, and I assume I haven't somehow snuck past the mutex in an unthread-safe way. (I made an attempt to test the threaded behavior, but ran into other issues trying to get a mutable reference into another thread at all.)

The weird issue is, I can't do the same for Index - there is no get_immut() I can use, and I haven't found another approach. I can get a mutable reference out of Mutex, but not an immutable one (and of course, I can't get the mutable one if I only have an immutable reference to begin with)

My expectation is that indexing would acquire a lock, and the returned reference (in both mutable and immutable cases) would maintain the lock for their lifetimes. As a bonus, it would be nice if RwLock-ed things could only grab/hold the read lock for the immutable cases, and the write lock for mutable ones.

For context as to why I'd do this: I have a Grid trait that is used by a bunch of different code, but backed by different implementations, some of which are thread-safe. I was hoping to put the Index and IndexMut traits on it for the nice syntax. Threads don't generally have mutable references to the thread-safe Grids at all, so the IndexMut trait would see little use there, but I could see it being valuable during setup or for the non-thread-safe cases. The immutable Index behavior seems like it would be useful everywhere.

Bonus question: I absolutely hate that Display code, how can I make it less hideous?

CodePudding user response:

If you look at the documentation of get_mut you'll see it's only possible precisely because a mutable reference ensures that there is no other reference or a lock to it, unfortunately for you that means that a get_ref for Mutex would only be possible by taking a mutable reference, that's just an artificially limited get_mut though.

Unfortunately for you since Index only gives you a shared reference you can't safely get a shared reference to it's contents, so you can't implement an Index so that it indexes into something behind a Mutex.

  • Related