Repository navigation
Tracking issue for Cell::update #50186
Description
Activity
- addedT-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]B-unstableBlocker: Implemented in the nightly compiler and unstable.Blocker: Implemented in the nightly compiler and unstable.
on Apr 24, 2018 Not a blocking issue for merging this as an unstable feature, so I'm registering my concern here:
The
updatefunction returning its new value feels very++i-ish. Similar to+=returning the result of the operation (which rust luckily does not!). I don't think we should be doing this. Options I see:- return
()(that was the suggestion in the PR) - make the closure argument a
&mut T, and allow the closure to return any value.- if users want the "return new/old value" behaviour, they can now encode it themselves via
|x| {*x += 1; *y }
- if users want the "return new/old value" behaviour, they can now encode it themselves via
Maybe there are other options?
Data point: the
volatilecrate'supdatemethod returns()(https://github.com/embed-rs/volatile/blob/master/src/lib.rs#L134)Reacted by Andrew, Yoann Arseneau, Stanislav Tkach, Zsombor Hollay-Horvath, ccde177, Taylor Cramer, OvermindDL1, George Tsiamasiotis, Fredrik Hammar, Cormac Relf and 7 more- return
The
T: Defaultflavor feels kinda awkward but I don’t know how to put that in more concrete terms, sorry :/It looks like there is a number of slightly different possible APIs for this. I don’t have a strong opinion on which is (or are) preferable.
- added a commit that references this issue
on Jul 23, 2018 Taking
&mutin this method would be awesome, I would love to use it.Unfortunately it seems that it would have to be
unsafe, because AFAIK there is no way in Rust to restrict usage like this:let c = Cell::new(vec![123]); c.update(|vec| { let x = &mut vec[0]; // x references 123 c.update(|vec| vec.clear()); // x references uninitialised memory *x = 456; // BOOM! undefined behavior })I hope, I'm wrong.
Reacted by tema3210, Yoann Arseneau, Jason Carr, Ben, jovenlin0527, Cole Tobin and Andrei Matveiakin@CodeSandwich it is a fundamental restriction of
Cell<T>that a reference&Tor&mut T(or anything else inside ofT) must not be given to the value inside the cell. It’s what makes it sound. (This is the difference withRefCell.)We could make an API that takes a closure that takes
&mut T, but that reference would not be to the inside of the cell. It would be to value that has been copied (withT: Copy) or moved (and temporarily replaced with some other value, withT: Default) onto the stack and will be copied/moved back when the closure returns. So code like your example would be arguably buggy but still sound: the innerupdate()would operate on a temporary value that would be overwritten at the end of the outer one.Reacted by Mara Bos, Sophie Herold, David James, Ross Wolf, Nicholas, Cole Tobin, Jesse Schalken and Alyssa HaroldsenI think, that a modifier method might be safe if we just forbid usage of reference to
Cell'sselfinside. Rust does not have mechanism for forbidding usage of a SPECIFIC reference, but it does have mechanism for forbidding usage of ALL references:'staticclosures.I've created a simple implementation of such method and I couldn't find any hole in it. It's a limited solution, but it's enough for many use cases including simple number and collections modifications.
A
'staticclosure could still reach theCell, for example by owning aRc.That's absolutely right :(
- addedC-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFC
on Oct 2, 2018 It seems like, the semantic are a bit different between
DefaultandCopy: the later is more light weighted, as it does not have to fill the hole behind with some temporary rubbish.What shall we name those variants?
update_movefor theDefaultcase?Also there is another variation:
update_unsafewhich will be unsafe and does not require a trait bound: it only moves the value and leave uninitialized value behind, before the method returns. This will make any nested calls UB and so have to be marked asunsafe.pub unsafe fn update_unsafe<F>(&self, f: F) -> T where F: FnOnce(T) -> T;
if you’re gonna use
unsafeanyway you can use.get()and manipulate the raw pointer. I don’t think we should facilitate it more than that.Reacted by Mara Bos, Jason Carr, Joshua Worth and David Hoppenbrouwers74 remaining items
I can't update the top post because it was created by a deleted user, but the API will be:
impl<T: Copy> Cell<T> { pub fn update(&self, f: impl FnOnce(T) -> T); }
Reacted by Gregory Petrosyan- added a commit that references this issue
on Apr 3, 2025 - added a commit that references this issue
on Apr 3, 2025 Crosslinking, FCP for the changed API is currently ongoing at #134446 (comment)
- added a commit that references this issue
on Apr 24, 2025 - added 2 commits that reference this issue
on Apr 25, 2025
This issue tracks the
cell_updatefeature.The feature adds
Cell::update, which allows one to more easily modify the inner value.For example, instead of
c.set(c.get() + 1)now you can just doc.update(|x| x + 1):