use std::cell::UnsafeCell; use std::fmt; use std::mem; use std::ops; use std::ptr; /// A container similar to [`std::cell::Cell`](http://doc.rust-lang.org/std/cell/struct.Cell.html), /// but that also supports not-implicitly-copyable types. pub struct MoveCell(UnsafeCell); impl MoveCell { /// Create a new `MoveCell` containing the given value. #[inline] pub fn new(value: T) -> MoveCell { MoveCell(UnsafeCell::new(value)) } /// Consume the `MoveCell` and return the inner value. #[inline] pub fn into_inner(self) -> T { unsafe { self.0.into_inner() } } /// Return the inner value after replacing it with the given value. #[inline] pub fn replace(&self, new_value: T) -> T { unsafe { mem::replace(&mut *self.0.get(), new_value) } } /// Returns a reference to the underlying `UnsafeCell`. /// /// ## Unsafety /// /// This method is unsafe because `UnsafeCell`'s field is public. #[inline] pub unsafe fn as_unsafe_cell(&self) -> &UnsafeCell { &self.0 } } impl Default for MoveCell { #[inline] fn default() -> MoveCell { MoveCell::new(T::default()) } } /// Convenience methods for when there is a default value. impl MoveCell { /// Return the inner value after replacing it with the default value. #[inline] pub fn take(&self) -> T { self.replace(T::default()) } /// Take the value, and return it in a `Borrow` guard that will return it when dropped. /// The cell’s contents are set to the default value until the guard is dropped. #[inline] pub fn borrow(&self) -> Borrow { Borrow { _cell: self, _value: self.take() } } } /// The cell’s contents are temporarily set to the default value during the clone. impl Clone for MoveCell { #[inline] fn clone(&self) -> MoveCell { MoveCell::new(self.borrow().clone()) } } /// The cell’s contents are temporarily set to the default value during the formatting. impl fmt::Debug for MoveCell { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "MoveCell({:?})", *self.borrow()) } } /// The cell’s contents are temporarily set to the default value during the comparaison. impl Eq for MoveCell {} /// The cell’s contents are temporarily set to the default value during the comparaison. impl PartialEq for MoveCell { #[inline] fn eq(&self, other: &MoveCell) -> bool { *self.borrow() == *other.borrow() } #[inline] fn ne(&self, other: &MoveCell) -> bool { *self.borrow() != *other.borrow() } } /// A wrapper for a value "borrowed" from a `MoveCell`. /// When the wrapper is dropped, the value is returned to the cell automatically. pub struct Borrow<'a, T: 'a> { _cell: &'a MoveCell, _value: T, } // Borrow intentionally does *not* implement Clone // so that borrow.clone() clones the inner value through auto-deref. impl<'a, T> Borrow<'a, T> { /// Consume the `Borrow` guard and return the value. pub fn into_inner(self) -> T { let value = unsafe { ptr::read(&self._value) }; mem::forget(self); value } } impl<'a, T> Drop for Borrow<'a, T> { fn drop(&mut self) { // FIXME: make self._value a `ManuallyDrop` when that exists. // https://github.com/rust-lang/rfcs/pull/197 mem::swap(&mut self._value, unsafe { &mut *self._cell.as_unsafe_cell().get() }) } } impl<'a, T> ops::Deref for Borrow<'a, T> { type Target = T; #[inline] fn deref(&self) -> &T { &self._value } } impl<'a, T> ops::DerefMut for Borrow<'a, T> { #[inline] fn deref_mut(&mut self) -> &mut T { &mut self._value } } impl<'a, T: fmt::Debug> fmt::Debug for Borrow<'a, T> { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "movecell::Borrow({:?})", self._value) } } #[test] fn it_works() { let x = MoveCell::new("first".to_owned()); assert_eq!(x.replace("second".to_owned()), "first"); assert_eq!(x.replace("third".to_owned()), "second"); assert_eq!(x.into_inner(), "third"); let x = MoveCell::new(Some("fourth".to_owned())); assert_eq!(x.take(), Some("fourth".to_owned())); assert_eq!(x.take(), None); assert_eq!(x.replace(Some("fifth".to_owned())), None); assert_eq!(&*x.borrow(), &Some("fifth".to_owned())); assert_eq!(x.borrow().as_ref().map(|s| s.len()), Some(5)); assert_eq!(x.borrow().clone(), Some("fifth".to_owned())); assert_eq!(x.borrow().is_some(), true); assert_eq!(x.borrow().is_none(), false); assert_eq!(x.clone(), x); assert_eq!(format!("{:?}", x), "MoveCell(Some(\"fifth\"))"); assert_eq!(format!("{:?}", x.borrow()), "movecell::Borrow(Some(\"fifth\"))"); assert_eq!(x.take(), Some("fifth".to_owned())); assert_eq!(x.borrow().is_some(), false); assert_eq!(x.borrow().is_none(), true); assert_eq!(&*x.borrow(), &None); assert_eq!(x.clone(), x); assert_eq!(format!("{:?}", x), "MoveCell(None)"); }