From 5ff433edc109d1e73f2278c6e657ad0a982241d0 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Fri, 9 Jun 2017 19:55:20 +0000 Subject: [PATCH 1/2] Create an RFC for RefCell::replace() --- text/0000-refcell-replace.md | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 text/0000-refcell-replace.md diff --git a/text/0000-refcell-replace.md b/text/0000-refcell-replace.md new file mode 100644 index 00000000000..35d281b5647 --- /dev/null +++ b/text/0000-refcell-replace.md @@ -0,0 +1,104 @@ +- Feature Name: refcell-replace +- Start Date: 2017-06-09 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Add dedicated methods to RefCell for replacing and swapping the contents. +These functions will panic if the RefCell is currently borrowed, +but will otherwise behave exactly like their cousins on Cell. + +# Motivation +[motivation]: #motivation + +The main problem this intends to solve is that doing a replace by hand +looks like this: + +```rust +let old_version = replace(&mut *some_refcell.borrow_mut(), new_version); +``` + +One of the most important parts of the ergonomics initiative has been reducing +"type tetris" exactly like that `&mut *`. + +It also seems weird that this use-case is so much cleaner with a plain `Cell`, +even though plain `Cell` is strictly a less popular abstraction. +Usually, people explain `RefCell` as being a superset of `Cell`, +but `RefCell` doesn't actually offer all of the functionality as seamlessly as `Cell`. + +# Detailed design +[design]: #detailed-design + +```rust +impl RefCell { + pub fn replace(&self, t: T) -> T { + mem::replace(&mut *self.borrow_mut(), t) + } + pub fn swap(&self, other: &Self) { + mem::swap(&mut *self.borrow_mut(), &mut *other.borrow_mut()) + } +} +``` + +# How We Teach This +[how-we-teach-this]: #how-we-teach-this + +The nicest aspect of this is that it maintains this story behind `Cell` and `RefCell`: + +> `RefCell` supports everything that `Cell` does. However, it has runtime overhead, +> and it can panic. + +# Drawbacks +[drawbacks]: #drawbacks + +Depending on how we want people to use RefCell, +this RFC might be removing deliberate syntactic vinegar. +For example, if RefCell is used to protect a counter: + +```rust +let counter_ref = counter.borrow_mut(); +*counter_ref += 1; +do_some_work(); +*counter_ref -= 1; +``` + +In this case, if `do_some_work()` tries to modify `counter`, it will panic. +Since Rust tends to value explicitness over implicitness exactly because it can surface bugs, +this code is conceptually more dangerous: + +```rust +counter.replace(counter.replace(0) + 1); +do_some_work(); +counter.replace(counter.replace(0) - 1); +``` + +Also, we're adding more specific functions to a core type. +That comes with cost in documentation and maintainance. + +# Alternatives +[alternatives]: #alternatives + +Besides just-write-the-reborrow, +these functions can also be put in a separate crate +with an extension trait. +This has all the disadvantages that two-line libraries usually have: + + * They tend to have low discoverability. + * They put strain on auditing. + * The hassle of adding an import and a toml line is as high as the reborrow. + +The other alternative, as far as getting rid of the reborrow goes, +is to change the language so that it implicitly does the reborrow. +That alternative is *massively* more general, +but it also has knock-on effects throughout the rest of the language. +It also still doesn't do anything about the asymetry between Cell and RefCell. + +# Unresolved questions +[unresolved]: #unresolved-questions + +Should we add `RefCell::get()` and `RefCell::set()`? +The equivalent versions with borrow(mut) and clone aren't as noisy, +since all the reborrowing is done implicitly because clone is a method, +but that would bring us all the way to RefCell-as-a-Cell-superset. From 87042ba89df03bbd798c324d648238084beafc06 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Wed, 5 Jul 2017 13:57:53 -0700 Subject: [PATCH 2/2] Fix a typo --- text/0000-refcell-replace.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-refcell-replace.md b/text/0000-refcell-replace.md index 35d281b5647..2adf950322d 100644 --- a/text/0000-refcell-replace.md +++ b/text/0000-refcell-replace.md @@ -24,7 +24,7 @@ One of the most important parts of the ergonomics initiative has been reducing "type tetris" exactly like that `&mut *`. It also seems weird that this use-case is so much cleaner with a plain `Cell`, -even though plain `Cell` is strictly a less popular abstraction. +even though plain `Cell` is strictly a less powerful abstraction. Usually, people explain `RefCell` as being a superset of `Cell`, but `RefCell` doesn't actually offer all of the functionality as seamlessly as `Cell`.