Auto merge of #39287 - wesleywiser:move_cell, r=aturon
Extend Cell to work with non-Copy types I'm not sure that I did this right but all of the tests pass. I also had to move the `new()` function so that `Cell`s with non-`Copy` `T`s could be created. That wasn't in the RFC but I assume it needed to be done?
This commit is contained in:
commit
5de2a24b2e
@ -118,7 +118,8 @@ These types are _generally_ found in struct fields, but they may be found elsewh
|
|||||||
|
|
||||||
## `Cell<T>`
|
## `Cell<T>`
|
||||||
|
|
||||||
[`Cell<T>`][cell] is a type that provides zero-cost interior mutability, but only for `Copy` types.
|
[`Cell<T>`][cell] is a type that provides zero-cost interior mutability by moving data in and
|
||||||
|
out of the cell.
|
||||||
Since the compiler knows that all the data owned by the contained value is on the stack, there's
|
Since the compiler knows that all the data owned by the contained value is on the stack, there's
|
||||||
no worry of leaking any data behind references (or worse!) by simply replacing the data.
|
no worry of leaking any data behind references (or worse!) by simply replacing the data.
|
||||||
|
|
||||||
@ -160,7 +161,7 @@ This relaxes the “no aliasing with mutability” restriction in places
|
|||||||
unnecessary. However, this also relaxes the guarantees that the restriction provides; so if your
|
unnecessary. However, this also relaxes the guarantees that the restriction provides; so if your
|
||||||
invariants depend on data stored within `Cell`, you should be careful.
|
invariants depend on data stored within `Cell`, you should be careful.
|
||||||
|
|
||||||
This is useful for mutating primitives and other `Copy` types when there is no easy way of
|
This is useful for mutating primitives and other types when there is no easy way of
|
||||||
doing it in line with the static rules of `&` and `&mut`.
|
doing it in line with the static rules of `&` and `&mut`.
|
||||||
|
|
||||||
`Cell` does not let you obtain interior references to the data, which makes it safe to freely
|
`Cell` does not let you obtain interior references to the data, which makes it safe to freely
|
||||||
@ -168,16 +169,17 @@ mutate.
|
|||||||
|
|
||||||
#### Cost
|
#### Cost
|
||||||
|
|
||||||
There is no runtime cost to using `Cell<T>`, however if you are using it to wrap larger (`Copy`)
|
There is no runtime cost to using `Cell<T>`, however if you are using it to wrap larger
|
||||||
structs, it might be worthwhile to instead wrap individual fields in `Cell<T>` since each write is
|
structs, it might be worthwhile to instead wrap individual fields in `Cell<T>` since each write is
|
||||||
otherwise a full copy of the struct.
|
otherwise a full copy of the struct.
|
||||||
|
|
||||||
|
|
||||||
## `RefCell<T>`
|
## `RefCell<T>`
|
||||||
|
|
||||||
[`RefCell<T>`][refcell] also provides interior mutability, but isn't restricted to `Copy` types.
|
[`RefCell<T>`][refcell] also provides interior mutability, but doesn't move data in and out of the
|
||||||
|
cell.
|
||||||
|
|
||||||
Instead, it has a runtime cost. `RefCell<T>` enforces the read-write lock pattern at runtime (it's
|
However, it has a runtime cost. `RefCell<T>` enforces the read-write lock pattern at runtime (it's
|
||||||
like a single-threaded mutex), unlike `&T`/`&mut T` which do so at compile time. This is done by the
|
like a single-threaded mutex), unlike `&T`/`&mut T` which do so at compile time. This is done by the
|
||||||
`borrow()` and `borrow_mut()` functions, which modify an internal reference count and return smart
|
`borrow()` and `borrow_mut()` functions, which modify an internal reference count and return smart
|
||||||
pointers which can be dereferenced immutably and mutably respectively. The refcount is restored when
|
pointers which can be dereferenced immutably and mutably respectively. The refcount is restored when
|
||||||
|
@ -15,10 +15,18 @@
|
|||||||
//! references. We say that `Cell<T>` and `RefCell<T>` provide 'interior mutability', in contrast
|
//! references. We say that `Cell<T>` and `RefCell<T>` provide 'interior mutability', in contrast
|
||||||
//! with typical Rust types that exhibit 'inherited mutability'.
|
//! with typical Rust types that exhibit 'inherited mutability'.
|
||||||
//!
|
//!
|
||||||
//! Cell types come in two flavors: `Cell<T>` and `RefCell<T>`. `Cell<T>` provides `get` and `set`
|
//! Cell types come in two flavors: `Cell<T>` and `RefCell<T>`. `Cell<T>` implements interior
|
||||||
//! methods that change the interior value with a single method call. `Cell<T>` though is only
|
//! mutability by moving values in and out of the `Cell<T>`. To use references instead of values,
|
||||||
//! compatible with types that implement `Copy`. For other types, one must use the `RefCell<T>`
|
//! one must use the `RefCell<T>` type, acquiring a write lock before mutating. `Cell<T>` provides
|
||||||
//! type, acquiring a write lock before mutating.
|
//! methods to retrieve and change the current interior value:
|
||||||
|
//!
|
||||||
|
//! - For types that implement `Copy`, the `get` method retrieves the current interior value.
|
||||||
|
//! - For types that implement `Default`, the `take` method replaces the current interior value
|
||||||
|
//! with `Default::default()` and returns the replaced value.
|
||||||
|
//! - For all types, the `replace` method replaces the current interior value and returns the
|
||||||
|
//! replaced value and the `into_inner` method consumes the `Cell<T>` and returns the interior
|
||||||
|
//! value. Additionally, the `set` method replaces the interior value, dropping the replaced
|
||||||
|
//! value.
|
||||||
//!
|
//!
|
||||||
//! `RefCell<T>` uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can
|
//! `RefCell<T>` uses Rust's lifetimes to implement 'dynamic borrowing', a process whereby one can
|
||||||
//! claim temporary, exclusive, mutable access to the inner value. Borrows for `RefCell<T>`s are
|
//! claim temporary, exclusive, mutable access to the inner value. Borrows for `RefCell<T>`s are
|
||||||
@ -176,9 +184,10 @@
|
|||||||
use cmp::Ordering;
|
use cmp::Ordering;
|
||||||
use fmt::{self, Debug, Display};
|
use fmt::{self, Debug, Display};
|
||||||
use marker::Unsize;
|
use marker::Unsize;
|
||||||
|
use mem;
|
||||||
use ops::{Deref, DerefMut, CoerceUnsized};
|
use ops::{Deref, DerefMut, CoerceUnsized};
|
||||||
|
|
||||||
/// A mutable memory location that admits only `Copy` data.
|
/// A mutable memory location.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more.
|
/// See the [module-level documentation](index.html) for more.
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
@ -187,23 +196,6 @@ pub struct Cell<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T:Copy> Cell<T> {
|
impl<T:Copy> Cell<T> {
|
||||||
/// Creates a new `Cell` containing the given value.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::cell::Cell;
|
|
||||||
///
|
|
||||||
/// let c = Cell::new(5);
|
|
||||||
/// ```
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
#[inline]
|
|
||||||
pub const fn new(value: T) -> Cell<T> {
|
|
||||||
Cell {
|
|
||||||
value: UnsafeCell::new(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a copy of the contained value.
|
/// Returns a copy of the contained value.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@ -221,25 +213,6 @@ impl<T:Copy> Cell<T> {
|
|||||||
unsafe{ *self.value.get() }
|
unsafe{ *self.value.get() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the contained value.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::cell::Cell;
|
|
||||||
///
|
|
||||||
/// let c = Cell::new(5);
|
|
||||||
///
|
|
||||||
/// c.set(10);
|
|
||||||
/// ```
|
|
||||||
#[inline]
|
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
|
||||||
pub fn set(&self, value: T) {
|
|
||||||
unsafe {
|
|
||||||
*self.value.get() = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a reference to the underlying `UnsafeCell`.
|
/// Returns a reference to the underlying `UnsafeCell`.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@ -378,6 +351,100 @@ impl<T: Copy> From<T> for Cell<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Cell<T> {
|
||||||
|
/// Creates a new `Cell` containing the given value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// let c = Cell::new(5);
|
||||||
|
/// ```
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(value: T) -> Cell<T> {
|
||||||
|
Cell {
|
||||||
|
value: UnsafeCell::new(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the contained value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// let c = Cell::new(5);
|
||||||
|
///
|
||||||
|
/// c.set(10);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
pub fn set(&self, val: T) {
|
||||||
|
let old = self.replace(val);
|
||||||
|
drop(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the contained value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(move_cell)]
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// let c = Cell::new(5);
|
||||||
|
/// let old = c.replace(10);
|
||||||
|
///
|
||||||
|
/// assert_eq!(5, old);
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "move_cell", issue = "39264")]
|
||||||
|
pub fn replace(&self, val: T) -> T {
|
||||||
|
mem::replace(unsafe { &mut *self.value.get() }, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwraps the value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(move_cell)]
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// let c = Cell::new(5);
|
||||||
|
/// let five = c.into_inner();
|
||||||
|
///
|
||||||
|
/// assert_eq!(five, 5);
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "move_cell", issue = "39264")]
|
||||||
|
pub fn into_inner(self) -> T {
|
||||||
|
unsafe { self.value.into_inner() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default> Cell<T> {
|
||||||
|
/// Takes the value of the cell, leaving `Default::default()` in its place.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #![feature(move_cell)]
|
||||||
|
/// use std::cell::Cell;
|
||||||
|
///
|
||||||
|
/// let c = Cell::new(5);
|
||||||
|
/// let five = c.take();
|
||||||
|
///
|
||||||
|
/// assert_eq!(five, 5);
|
||||||
|
/// assert_eq!(c.into_inner(), 0);
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "move_cell", issue = "39264")]
|
||||||
|
pub fn take(&self) -> T {
|
||||||
|
self.replace(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[unstable(feature = "coerce_unsized", issue = "27732")]
|
#[unstable(feature = "coerce_unsized", issue = "27732")]
|
||||||
impl<T: CoerceUnsized<U>, U> CoerceUnsized<Cell<U>> for Cell<T> {}
|
impl<T: CoerceUnsized<U>, U> CoerceUnsized<Cell<U>> for Cell<T> {}
|
||||||
|
|
||||||
|
@ -209,6 +209,37 @@ fn cell_default() {
|
|||||||
assert_eq!(0, cell.get());
|
assert_eq!(0, cell.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cell_set() {
|
||||||
|
let cell = Cell::new(10);
|
||||||
|
cell.set(20);
|
||||||
|
assert_eq!(20, cell.get());
|
||||||
|
|
||||||
|
let cell = Cell::new("Hello".to_owned());
|
||||||
|
cell.set("World".to_owned());
|
||||||
|
assert_eq!("World".to_owned(), cell.into_inner());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cell_replace() {
|
||||||
|
let cell = Cell::new(10);
|
||||||
|
assert_eq!(10, cell.replace(20));
|
||||||
|
assert_eq!(20, cell.get());
|
||||||
|
|
||||||
|
let cell = Cell::new("Hello".to_owned());
|
||||||
|
assert_eq!("Hello".to_owned(), cell.replace("World".to_owned()));
|
||||||
|
assert_eq!("World".to_owned(), cell.into_inner());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cell_into_inner() {
|
||||||
|
let cell = Cell::new(10);
|
||||||
|
assert_eq!(10, cell.into_inner());
|
||||||
|
|
||||||
|
let cell = Cell::new("Hello world".to_owned());
|
||||||
|
assert_eq!("Hello world".to_owned(), cell.into_inner());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn refcell_default() {
|
fn refcell_default() {
|
||||||
let cell: RefCell<u64> = Default::default();
|
let cell: RefCell<u64> = Default::default();
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
#![feature(unique)]
|
#![feature(unique)]
|
||||||
#![feature(ordering_chaining)]
|
#![feature(ordering_chaining)]
|
||||||
#![feature(ptr_unaligned)]
|
#![feature(ptr_unaligned)]
|
||||||
|
#![feature(move_cell)]
|
||||||
|
|
||||||
extern crate core;
|
extern crate core;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
Loading…
Reference in New Issue
Block a user