Skip to content

Commit 7e18479

Browse files
committed
Add 'take', 'replace', 'replace_with' from std::cell::RefCell
This changeset also clarifies the language around mutable borrows. The message accountable_refcell paniced with when trying to take a mutable borrow was not entirely accurate ("RefCell is already immutably borrowed"), as panics can also occur when trying to mutably borrow a RefCell that is already mutably borrowed. Technically, at the time of this writing, the `take` method is not in stable Rust, but will be in the next version (1.49) rust-lang/rust#71395 A rust-toolchain file has been added codifying the expectation that this crate targets stable Rust.
1 parent be1ad50 commit 7e18479

File tree

2 files changed

+104
-5
lines changed

2 files changed

+104
-5
lines changed

rust-toolchain

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
stable

src/lib.rs

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use backtrace::Backtrace;
88
use std::cell::{
99
BorrowError, BorrowMutError, Ref as StdRef, RefCell as StdRefCell, RefMut as StdRefMut,
1010
};
11-
use std::env;
11+
use std::{env, mem};
1212
use std::fmt::{Debug, Display, Error, Formatter};
1313
use std::ops::{Deref, DerefMut};
1414

@@ -203,8 +203,8 @@ impl<T: ?Sized> RefCell<T> {
203203
})
204204
}
205205

206-
/// Borrow the value stored in this cell mutably. Panics if any outstanding immutable
207-
/// borrows of the same cell exist.
206+
/// Borrow the value stored in this cell mutably. Panics if there are any other outstanding
207+
/// borrows of this cell (mutable borrows are unique, i.e. there can only be one).
208208
pub fn borrow_mut(&self) -> RefMut<T> {
209209
if let Ok(r) = self.inner.try_borrow_mut() {
210210
let id = self.borrows.borrow_mut().record();
@@ -225,7 +225,7 @@ impl<T: ?Sized> RefCell<T> {
225225
}
226226
}
227227
}
228-
panic!("RefCell is already immutably borrowed.");
228+
panic!("RefCell is already borrowed.");
229229
}
230230
}
231231

@@ -251,6 +251,20 @@ impl<T: ?Sized> RefCell<T> {
251251
}
252252
}
253253

254+
impl <T> RefCell<T> {
255+
/// Corresponds to https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.replace.
256+
pub fn replace(&self, t: T) -> T {
257+
mem::replace(&mut *self.borrow_mut(), t)
258+
}
259+
260+
/// Corresponds to https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.replace_with.
261+
pub fn replace_with<F: FnOnce(&mut T) -> T>(&self, f: F) -> T {
262+
let mut_borrow = &mut *self.borrow_mut();
263+
let replacement = f(mut_borrow);
264+
mem::replace(mut_borrow, replacement)
265+
}
266+
}
267+
254268
/// Print a backtrace without any frames from the backtrace library.
255269
fn print_filtered_backtrace(backtrace: &Backtrace) {
256270
let mut idx = 1;
@@ -284,6 +298,13 @@ impl<T: Clone> Clone for RefCell<T> {
284298
}
285299
}
286300

301+
impl <T: Default> RefCell<T> {
302+
/// Corresponds to https://doc.rust-lang.org/std/cell/struct.RefCell.html#method.take.
303+
pub fn take(&self) -> T {
304+
self.replace(Default::default())
305+
}
306+
}
307+
287308
impl<T: Default> Default for RefCell<T> {
288309
fn default() -> RefCell<T> {
289310
RefCell::new(Default::default())
@@ -319,7 +340,7 @@ mod tests {
319340
use super::{Ref, RefCell};
320341

321342
#[test]
322-
#[should_panic(expected = "RefCell is already immutably borrowed")]
343+
#[should_panic(expected = "RefCell is already borrowed")]
323344
fn cannot_borrow_mutably() {
324345
let c = RefCell::new(5);
325346
let _b = c.borrow();
@@ -358,4 +379,81 @@ mod tests {
358379
};
359380
let _b2 = c.borrow_mut();
360381
}
382+
383+
#[test]
384+
fn take_refcell_returns_correct_value() {
385+
let c: RefCell<i32> = RefCell::new(5);
386+
assert_eq!(5, c.take());
387+
assert_eq!(i32::default(), *c.borrow());
388+
}
389+
390+
#[test]
391+
#[should_panic(expected = "RefCell is already borrowed")]
392+
fn cannot_take_borrowed_refcell() {
393+
let c = RefCell::new(5);
394+
let _b = c.borrow();
395+
c.take();
396+
}
397+
398+
#[test]
399+
#[should_panic(expected = "RefCell is already borrowed")]
400+
fn cannot_take_mut_borrowed_refcell() {
401+
let c = RefCell::new(5);
402+
let _b = c.borrow_mut();
403+
c.take();
404+
}
405+
406+
#[test]
407+
fn replace_refcell_properly_replaces_contents() {
408+
let c = RefCell::new(5);
409+
c.replace(12);
410+
assert_eq!(12, *c.borrow());
411+
}
412+
413+
#[test]
414+
#[should_panic(expected = "RefCell is already borrowed")]
415+
fn cannot_replace_borrowed_refcell() {
416+
let c = RefCell::new(5);
417+
let _b = c.borrow();
418+
c.replace(12);
419+
}
420+
421+
#[test]
422+
#[should_panic(expected = "RefCell is already borrowed")]
423+
fn cannot_replace_mut_borrowed_refcell() {
424+
let c = RefCell::new(5);
425+
let _b = c.borrow_mut();
426+
c.replace(12);
427+
}
428+
429+
#[test]
430+
fn replace_with_refcell_properly_replaces_contents() {
431+
let c = RefCell::new(5);
432+
c.replace_with(|&mut old_value| old_value + 1);
433+
assert_eq!(6, *c.borrow());
434+
}
435+
436+
#[test]
437+
#[should_panic(expected = "RefCell is already borrowed")]
438+
fn cannot_replace_with_borrowed_refcell() {
439+
let c = RefCell::new(5);
440+
let _b = c.borrow();
441+
c.replace_with(|&mut old_val| { old_val + 1 });
442+
}
443+
444+
#[test]
445+
#[should_panic(expected = "RefCell is already borrowed")]
446+
fn cannot_replace_with_mut_borrowed_refcell() {
447+
let c = RefCell::new(5);
448+
let _b = c.borrow_mut();
449+
c.replace_with(|&mut old_val| { old_val + 1 });
450+
}
451+
452+
#[test]
453+
#[should_panic(expected = "RefCell is already borrowed")]
454+
fn test() {
455+
let c = RefCell::new(5);
456+
let _b = c.borrow_mut();
457+
let _b2 = c.borrow_mut();
458+
}
361459
}

0 commit comments

Comments
 (0)