What is the point of using cell since mutable reference can be obtained using Rc::get_mut?

I am trying to understand what is the point of cell since we can simply to a Rc::get_mut in multiple ownership scenarios and directly mutate the thing itself in single ownership cases

1 Like

Rc::get_mut does not work if you are actually sharing the value, it only works if you have a unique Rc:

Returns None otherwise, because it is not safe to mutate a shared value.

1 Like

Oh makes sense. When I try to put a Cell inside an Rc however I am getting an error saying get_mut found for Rc. This is so frustrating I ended up using raw pointers instead which are apparently much nicer to work with

I don't really understand what error you're getting, but if it is due to calling the wrong get_mut, you usually would resolve that by calling the method with some extra qualifiers like Cell::get_mut(blah) or some such.

But you typically wouldn't put a Cell in Arc anyway, because Cell (and RefCell) are not thread safe:

Consider Arc< RefCell<T> > . RefCell<T> isn't Sync , and if Arc<T> was always Send , Arc< RefCell<T> > would be as well. But then we'd have a problem: RefCell<T> is not thread safe; it keeps track of the borrowing count using non-atomic operations.

So you'd really want something like Arc<Mutex<T>> or Arc<RwLock<T>>. RefCell and Cell are usable in Rc, however.

It was a typo sorry I was referring to Rc but seems I understood what was going wrong I am migrating to Rc<Box>> from raw pointers now.
Is there any performance penalty using Rc and cell over raw pointers?

@skysch


this is the problem seems I am not able to use get_mut of Cell under Rc

Rc is doing reference counting, so there is overhead for storing the count, reading it, and modifying it. Raw pointers won't have that problem. RefCell has to keep track if the data is already being borrowed, so there's some overhead in checking that. (For instance, if there is a borrow, the attempt will panic, so the code has to do a conditional branch.) I don't think there's any overhead for Cell (I never wanted to use it), but it only works on copy types.

Raw pointers will outperform most of this, but you lose the safety of having ownership and borrowing modeled in the type system, which means you have to be careful to avoid the pitfalls of managing memory manually and racy data accesses and whatnot.

Also, Rc<Box<T>> is redundant, because Rc already does everything that Box does.

2 Likes

Cell is supposed to provide internal mutability in an immutable context however in order to use get_mut it requires a mutable reference This is confusing If I can get a mutable ref to the value why would I want to use Cell in the first place

Yes, that's confusing, but you're just using the wrong method. Cell::get_mut is for getting the value when you know you have unique access to it (because that is quicker.) You probably want to be calling get and set instead, because that is how you are supposed to modify the value through a shared reference.

1 Like

How is it quicker than defining the value mutably ?
let mut a =5;
let mut a = Cell::new(5);
in second case the variable still needs to be mutable to be able to use get_mut but I needed to type Cell::new in front of it. I cant understand the point of cell here

You would only call Cell::get_mut if you were using a cell for other reasons, but knew in some particular context that you had unique access to it. Like if you were in the destructor of some object that held a Cell inside it.

1 Like

So what i need to do is to first get the thing modify it and then set it is that correct ?

and seems it is a better idea to use raw pointers in this case since this is an opaque type and the block is very small

Yes, if you wanted to modify a value in a Cell, you would typically call get, modify the result, then write it back using set.

I can't really agree or disagree with that without knowing more about what you're doing, but if you are more comfortable using raw pointers, that's not much of a problem. Just make sure you don't try to convert them to references without understanding what that entails.

I am getting raw pointers from a box and converting back to a box when I am done assuming Box::into_raw(Box::new(Foo{}))
does the same thing as calloc(1,sizeof(Foo))

Nothing wrong with that as far as I know... However, as I mentioned before, Rc<Box<T>> is redundant, and best avoided. Rc is already a pointer, so an Rc<Box<T>> is a double indirection and highly inefficient. (Rc also has into_raw and from_raw if you need access to its internal pointer.)

I just got rid of the cell and rc all together and using Box only.

1 Like

Not true, you can use any type in Cell, it's just nicer to use with Copy or Default types.

2 Likes

@NikosEfthias before wading into unsafe I reccomend that you read the Rust Nomicon

1 Like

Thanks I will be reading it and what I am doing is a part of a programming challenge I will be sharing the code for review here as well