about summary refs log tree commit diff
path: root/src/libcore
diff options
context:
space:
mode:
authorkennytm <kennytm@gmail.com>2018-04-24 11:57:00 +0800
committerGitHub <noreply@github.com>2018-04-24 11:57:00 +0800
commit91cc872987da776874021d985f08a4262ac07396 (patch)
treeef1155a6ed17d4c4df03df892e102c6974d0ac36 /src/libcore
parentcefdd6d5e99618f193c1cd3365aa18b786731d97 (diff)
parent29e9de85d6ae185d7d66c7ba0f2c418082d2df5f (diff)
downloadrust-91cc872987da776874021d985f08a4262ac07396.tar.gz
rust-91cc872987da776874021d985f08a4262ac07396.zip
Rollup merge of #49727 - stjepang:cell-update, r=SimonSapin
Add Cell::update

This commit adds a new method `Cell::update`, which applies a function to the value inside the cell.

Previously discussed in: https://github.com/rust-lang/rfcs/issues/2171

### Motivation

Updating `Cell`s is currently a bit verbose. Here are several real examples (taken from rustc and crossbeam):

```rust
self.print_fuel.set(self.print_fuel.get() + 1);

self.diverges.set(self.diverges.get() | Diverges::Always);

let guard_count = self.guard_count.get();
self.guard_count.set(guard_count.checked_add(1).unwrap());
if guard_count == 0 {
    // ...
}
```

With the addition of the new method `Cell::update`, this code can be simplified to:

```rust
self.print_fuel.update(|x| x + 1);

self.diverges.update(|x| x | Diverges::Always);

if self.guard_count.update(|x| x.checked_add(1).unwrap()) == 1 {
    // ...
}
```

### Unresolved questions

1. Should we return the old value instead of the new value (like in `fetch_add` and `fetch_update`)?
2. Should the return type simply be `()`?
3. Naming: `update` vs `modify` vs `mutate` etc.

cc @SimonSapin
Diffstat (limited to 'src/libcore')
-rw-r--r--src/libcore/cell.rs27
-rw-r--r--src/libcore/tests/cell.rs11
-rw-r--r--src/libcore/tests/lib.rs1
3 files changed, 39 insertions, 0 deletions
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index c8ee166fee3..1ff187ed3f1 100644
--- a/src/libcore/cell.rs
+++ b/src/libcore/cell.rs
@@ -256,6 +256,33 @@ impl<T:Copy> Cell<T> {
     pub fn get(&self) -> T {
         unsafe{ *self.value.get() }
     }
+
+    /// Updates the contained value using a function and returns the new value.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(cell_update)]
+    ///
+    /// use std::cell::Cell;
+    ///
+    /// let c = Cell::new(5);
+    /// let new = c.update(|x| x + 1);
+    ///
+    /// assert_eq!(new, 6);
+    /// assert_eq!(c.get(), 6);
+    /// ```
+    #[inline]
+    #[unstable(feature = "cell_update", issue = "50186")]
+    pub fn update<F>(&self, f: F) -> T
+    where
+        F: FnOnce(T) -> T,
+    {
+        let old = self.get();
+        let new = f(old);
+        self.set(new);
+        new
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/src/libcore/tests/cell.rs b/src/libcore/tests/cell.rs
index cc0ef6a6f17..962fb2f0e02 100644
--- a/src/libcore/tests/cell.rs
+++ b/src/libcore/tests/cell.rs
@@ -27,6 +27,17 @@ fn smoketest_cell() {
 }
 
 #[test]
+fn cell_update() {
+    let x = Cell::new(10);
+
+    assert_eq!(x.update(|x| x + 5), 15);
+    assert_eq!(x.get(), 15);
+
+    assert_eq!(x.update(|x| x / 3), 5);
+    assert_eq!(x.get(), 5);
+}
+
+#[test]
 fn cell_has_sensible_show() {
     let x = Cell::new("foo bar");
     assert!(format!("{:?}", x).contains(x.get()));
diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs
index 7d3852d2f2a..2b69f04013d 100644
--- a/src/libcore/tests/lib.rs
+++ b/src/libcore/tests/lib.rs
@@ -10,6 +10,7 @@
 
 #![feature(ascii_ctype)]
 #![feature(box_syntax)]
+#![feature(cell_update)]
 #![feature(core_float)]
 #![feature(core_private_bignum)]
 #![feature(core_private_diy_float)]