diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index e56b631dbaf8d..d7394fcb50536 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -435,8 +435,15 @@ impl Cell { // but `Cell` is `!Sync` so this won't happen. unsafe { *self.value.get() } } +} - /// Updates the contained value using a function and returns the new value. +impl Cell { + /// Updates the contained value using a function. + /// + /// If `T` implements [`Copy`], the function is called on a copy of the + /// contained value. Otherwise, if `T` implements [`Default`], the value + /// contained in the cell is temporarily replaced by [`Default::default()`] + /// while the function runs. /// /// # Examples /// @@ -445,22 +452,78 @@ impl Cell { /// /// use std::cell::Cell; /// - /// let c = Cell::new(5); - /// let new = c.update(|x| x + 1); + /// let a = Cell::new(5); + /// a.update(|x| x + 1); /// - /// assert_eq!(new, 6); - /// assert_eq!(c.get(), 6); + /// let b = Cell::new("Hello".to_string()); + /// b.update(|x| x + " World!"); + /// + /// assert_eq!(a.get(), 6); + /// assert_eq!(b.take(), "Hello World!"); /// ``` #[inline] #[unstable(feature = "cell_update", issue = "50186")] - pub fn update(&self, f: F) -> T + pub fn update(&self, f: F) where F: FnOnce(T) -> T, + T: get_or_take::CopyOrDefault, { - let old = self.get(); + let old = ::get_or_take(self); let new = f(old); self.set(new); - new + } +} + +#[unstable(feature = "cell_update_specializations", issue = "none")] +mod get_or_take { + use super::Cell; + + #[rustc_on_unimplemented( + label = "`{Self}` implements neither Copy nor Default", + message = "`Cell<{Self}>::update()` requires either `{Self}: Copy` or `{Self}: Default`" + )] + #[marker] + pub trait CopyOrDefault: Sized {} + + impl CopyOrDefault for T {} + impl CopyOrDefault for T {} + + #[rustc_unsafe_specialization_marker] + trait IsDefault: Default {} + impl IsDefault for T {} + + pub(super) trait CopySpec { + fn get_or_take(cell: &Cell) -> Self; + } + + trait DefaultSpec { + fn get_or_take(cell: &Cell) -> Self; + } + + impl CopySpec for T { + default fn get_or_take(cell: &Cell) -> Self { + ::get_or_take(cell) + } + } + + impl CopySpec for T { + fn get_or_take(cell: &Cell) -> Self { + cell.get() + } + } + + impl DefaultSpec for T { + default fn get_or_take(_: &Cell) -> Self { + // This is unreachable because we'd only get here for types that + // implement CopyOrDefault, but not Copy or Default, which isn't possible. + unreachable!() + } + } + + impl DefaultSpec for T { + fn get_or_take(cell: &Cell) -> Self { + cell.take() + } } } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 265ba9f1bb91b..b4303902d2b69 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -140,6 +140,7 @@ #![feature(lang_items)] #![feature(link_llvm_intrinsics)] #![feature(llvm_asm)] +#![feature(marker_trait_attr)] #![feature(min_specialization)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/library/core/tests/cell.rs b/library/core/tests/cell.rs index 85a006c5d5bef..311d8f5e5286f 100644 --- a/library/core/tests/cell.rs +++ b/library/core/tests/cell.rs @@ -52,11 +52,16 @@ fn smoketest_cell() { fn cell_update() { let x = Cell::new(10); - assert_eq!(x.update(|x| x + 5), 15); + x.update(|x| x + 5); assert_eq!(x.get(), 15); - assert_eq!(x.update(|x| x / 3), 5); + x.update(|x| x / 3); assert_eq!(x.get(), 5); + + let x = Cell::new("abc".to_string()); + + x.update(|x| x + "123"); + assert_eq!(x.take(), "abc123"); } #[test]