Is there a way to implement Index/IndexMut on a Vec<Cell<f32>>

  1. Yes, by some issue beyond the scope of this post, I am forced to use Vec<Cell<f32>> there isn't a nice way around this. (I'm implementing a tensor library; and more than one "view" can share an underlying "storage").

  2. It gets tiring after a while to write things ilke:

foo[idx].set( foo[idx*2].get() + bar[idx*3].get());

when instead I really just want to write

foo[idx] = foo[idx*2] + bar[idx*3]
  1. Here is what I have so far:

pub struct VecCell{
    pub data: Vec<Cell<f32>>,
}

impl Index<usize> for VecCell {
    type Output = f32;

    fn index(&self, index: usize) -> &f32 {
        // the &f32 ret type is forced by Index trait
        &self.data[index].get()
        // $&@*@ now we are trying to return ref to temporarl
    }
}

impl IndexMut<usize> for VecCell {
    fn index_mut(&mut self, index: usize) -> &mut f32 {
        // ????
    }
}

You aren't able to use Vec<RefCell<f32>>, only Cell?

Cell doesn't allow references so there is no such functionality. Maybe a helper macro could be used, (not sure any would make it clearer to read/write though.)

IndexMut seems possible thanks to get_mut() on the Cell. This is allowed, because all the &muts require exclusive access.

However, I'm surprised that IndexMut requires existence of Index.

And Index is not possible. This is because Cell allows shared mutable access as long as its done via .set(). However, if it exposed shared reference to its contents, another call to .set() could change value of the shared immutable reference. Rust can't allow that.

&self.data[index].get()

doesn't compile, because it's equivalent of:

let tmp = self.data[index].get(); // it's a *copy* that lives on stack
return &tmp; // the stack variable will be destroyed before the function returns

Rust doesn't have traits for indexing that set or return things by value. I'm not sure why.

1 Like

@kornel : Thanks for the detailed analysis.

Is the following issue related?

A &f32 is expect to "not change", and thus is something Rustc can assume to be constant while doing optimizantions. On the other hand, the contents of a Cell are "volatile", and there is no safe way to get a const ref to the "inside" of the Cell ?

Yes. That's why Cell allows you to get a copy, but not a shared reference to the inner value.

1 Like

If I understand correctly, the "return-by-value" behaviour could be used in two cases:

  • the returned value is moved from the storage being indexed, like in Option::take(), with transferring ownership to the caller;
  • the returned value is copied.

First case is undesirable in many cases and often directly impossible (for example, how should behave indexing into Vec if indexing takes ownership of value?). Second case used implicitly is impossible for non-Copy types, and if we're going explicit, it's already possible, AFAIK, by cloning the value behind given reference.