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
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.
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'tSync
, and ifArc<T>
was alwaysSend
,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?
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.
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.
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.
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.)
Not true, you can use any type in Cell
, it's just nicer to use with Copy
or Default
types.
@NikosEfthias before wading into unsafe I reccomend that you read the Rust Nomicon
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