From 3db5f345ae6ec9016ae6457200f28bda6b6f48f9 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 29 Sep 2021 15:30:38 +0200 Subject: [PATCH 1/3] Make `Cell::update` work for `T: Default | Copy`. --- library/core/src/cell.rs | 79 ++++++++++++++++++++++++++++++++++++---- library/core/src/lib.rs | 1 + 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index e56b631dbaf8d..fdf0f8f6adeb3 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 + /// contain 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)] From a9c772aaf0d32136d5c74e195b8af4f9ee9451f5 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 29 Sep 2021 15:48:22 +0200 Subject: [PATCH 2/3] Update test. --- library/core/tests/cell.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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] From bb98c58f16eaf38a6a84e43a285e94e0c51e59cb Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 29 Sep 2021 16:47:39 +0200 Subject: [PATCH 3/3] Fix typos in Cell::update docs. Co-authored-by: fee1-dead --- library/core/src/cell.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index fdf0f8f6adeb3..d7394fcb50536 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -441,8 +441,8 @@ impl Cell { /// Updates the contained value using a function. /// /// If `T` implements [`Copy`], the function is called on a copy of the - /// contain value. Otherwise, if `T` implements [`Default`], the value - /// contained in the cell is temporarily replaced by [`Default::default()]` + /// contained value. Otherwise, if `T` implements [`Default`], the value + /// contained in the cell is temporarily replaced by [`Default::default()`] /// while the function runs. /// /// # Examples