diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 3eb1fa4a558..c5d08c3dd20 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -146,7 +146,7 @@ use clone::Clone; use cmp::{PartialEq, Eq}; use default::Default; use marker::{Copy, Send, Sync, Sized}; -use ops::{Deref, DerefMut, Drop}; +use ops::{Deref, DerefMut, Drop, FnOnce}; use option::Option; use option::Option::{None, Some}; @@ -551,13 +551,161 @@ impl<'b, T: ?Sized> Deref for Ref<'b, T> { /// /// A `Clone` implementation would interfere with the widespread /// use of `r.borrow().clone()` to clone the contents of a `RefCell`. +#[deprecated(since = "1.2.0", reason = "moved to a `Ref::clone` associated function")] #[unstable(feature = "core", reason = "likely to be moved to a method, pending language changes")] #[inline] pub fn clone_ref<'b, T:Clone>(orig: &Ref<'b, T>) -> Ref<'b, T> { - Ref { - _value: orig._value, - _borrow: orig._borrow.clone(), + Ref::clone(orig) +} + +impl<'b, T: ?Sized> Ref<'b, T> { + /// Copies a `Ref`. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `Ref::clone(...)`. + /// A `Clone` implementation or a method would interfere with the widespread + /// use of `r.borrow().clone()` to clone the contents of a `RefCell`. + #[unstable(feature = "cell_extras", + reason = "likely to be moved to a method, pending language changes")] + #[inline] + pub fn clone(orig: &Ref<'b, T>) -> Ref<'b, T> { + Ref { + _value: orig._value, + _borrow: orig._borrow.clone(), + } + } + + /// Make a new `Ref` for a component of the borrowed data. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `Ref::map(...)`. + /// A method would interfere with methods of the same name on the contents of a `RefCell` + /// used through `Deref`. + /// + /// # Example + /// + /// ``` + /// # #![feature(cell_extras)] + /// use std::cell::{RefCell, Ref}; + /// + /// let c = RefCell::new((5, 'b')); + /// let b1: Ref<(u32, char)> = c.borrow(); + /// let b2: Ref = Ref::map(b1, |t| &t.0); + /// assert_eq!(*b2, 5) + /// ``` + #[unstable(feature = "cell_extras", reason = "recently added")] + #[inline] + pub fn map(orig: Ref<'b, T>, f: F) -> Ref<'b, U> + where F: FnOnce(&T) -> &U + { + Ref { + _value: f(orig._value), + _borrow: orig._borrow, + } + } + + /// Make a new `Ref` for a optional component of the borrowed data, e.g. an enum variant. + /// + /// The `RefCell` is already immutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `Ref::filter_map(...)`. + /// A method would interfere with methods of the same name on the contents of a `RefCell` + /// used through `Deref`. + /// + /// # Example + /// + /// ``` + /// # #![feature(cell_extras)] + /// use std::cell::{RefCell, Ref}; + /// + /// let c = RefCell::new(Ok(5)); + /// let b1: Ref> = c.borrow(); + /// let b2: Ref = Ref::filter_map(b1, |o| o.as_ref().ok()).unwrap(); + /// assert_eq!(*b2, 5) + /// ``` + #[unstable(feature = "cell_extras", reason = "recently added")] + #[inline] + pub fn filter_map(orig: Ref<'b, T>, f: F) -> Option> + where F: FnOnce(&T) -> Option<&U> + { + f(orig._value).map(move |new| Ref { + _value: new, + _borrow: orig._borrow, + }) + } +} + +impl<'b, T: ?Sized> RefMut<'b, T> { + /// Make a new `RefMut` for a component of the borrowed data, e.g. an enum variant. + /// + /// The `RefCell` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `RefMut::map(...)`. + /// A method would interfere with methods of the same name on the contents of a `RefCell` + /// used through `Deref`. + /// + /// # Example + /// + /// ``` + /// # #![feature(cell_extras)] + /// use std::cell::{RefCell, RefMut}; + /// + /// let c = RefCell::new((5, 'b')); + /// { + /// let b1: RefMut<(u32, char)> = c.borrow_mut(); + /// let mut b2: RefMut = RefMut::map(b1, |t| &mut t.0); + /// assert_eq!(*b2, 5); + /// *b2 = 42; + /// } + /// assert_eq!(*c.borrow(), (42, 'b')); + /// ``` + #[unstable(feature = "cell_extras", reason = "recently added")] + #[inline] + pub fn map(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U> + where F: FnOnce(&mut T) -> &mut U + { + RefMut { + _value: f(orig._value), + _borrow: orig._borrow, + } + } + + /// Make a new `RefMut` for a optional component of the borrowed data, e.g. an enum variant. + /// + /// The `RefCell` is already mutably borrowed, so this cannot fail. + /// + /// This is an associated function that needs to be used as `RefMut::filter_map(...)`. + /// A method would interfere with methods of the same name on the contents of a `RefCell` + /// used through `Deref`. + /// + /// # Example + /// + /// ``` + /// # #![feature(cell_extras)] + /// use std::cell::{RefCell, RefMut}; + /// + /// let c = RefCell::new(Ok(5)); + /// { + /// let b1: RefMut> = c.borrow_mut(); + /// let mut b2: RefMut = RefMut::filter_map(b1, |o| o.as_mut().ok()).unwrap(); + /// assert_eq!(*b2, 5); + /// *b2 = 42; + /// } + /// assert_eq!(*c.borrow(), Ok(42)); + /// ``` + #[unstable(feature = "cell_extras", reason = "recently added")] + #[inline] + pub fn filter_map(orig: RefMut<'b, T>, f: F) -> Option> + where F: FnOnce(&mut T) -> Option<&mut U> + { + let RefMut { _value, _borrow } = orig; + f(_value).map(move |new| RefMut { + _value: new, + _borrow: _borrow, + }) } } diff --git a/src/libcoretest/cell.rs b/src/libcoretest/cell.rs index f02312b8641..20740a5e2ce 100644 --- a/src/libcoretest/cell.rs +++ b/src/libcoretest/cell.rs @@ -115,13 +115,13 @@ fn discard_doesnt_unborrow() { } #[test] -fn clone_ref_updates_flag() { +fn ref_clone_updates_flag() { let x = RefCell::new(0); { let b1 = x.borrow(); assert_eq!(x.borrow_state(), BorrowState::Reading); { - let _b2 = clone_ref(&b1); + let _b2 = Ref::clone(&b1); assert_eq!(x.borrow_state(), BorrowState::Reading); } assert_eq!(x.borrow_state(), BorrowState::Reading); @@ -129,6 +129,82 @@ fn clone_ref_updates_flag() { assert_eq!(x.borrow_state(), BorrowState::Unused); } +#[test] +fn ref_map_does_not_update_flag() { + let x = RefCell::new(Some(5)); + { + let b1: Ref> = x.borrow(); + assert_eq!(x.borrow_state(), BorrowState::Reading); + { + let b2: Ref = Ref::map(b1, |o| o.as_ref().unwrap()); + assert_eq!(*b2, 5); + assert_eq!(x.borrow_state(), BorrowState::Reading); + } + assert_eq!(x.borrow_state(), BorrowState::Unused); + } + assert_eq!(x.borrow_state(), BorrowState::Unused); +} + +#[test] +fn ref_map_accessor() { + struct X(RefCell<(u32, char)>); + impl X { + fn accessor(&self) -> Ref { + Ref::map(self.0.borrow(), |tuple| &tuple.0) + } + } + let x = X(RefCell::new((7, 'z'))); + let d: Ref = x.accessor(); + assert_eq!(*d, 7); +} + +#[test] +fn ref_filter_map_accessor() { + struct X(RefCell>); + impl X { + fn accessor(&self) -> Option> { + Ref::filter_map(self.0.borrow(), |r| r.as_ref().ok()) + } + } + let x = X(RefCell::new(Ok(7))); + let d: Ref = x.accessor().unwrap(); + assert_eq!(*d, 7); +} + +#[test] +fn ref_mut_map_accessor() { + struct X(RefCell<(u32, char)>); + impl X { + fn accessor(&self) -> RefMut { + RefMut::map(self.0.borrow_mut(), |tuple| &mut tuple.0) + } + } + let x = X(RefCell::new((7, 'z'))); + { + let mut d: RefMut = x.accessor(); + assert_eq!(*d, 7); + *d += 1; + } + assert_eq!(*x.0.borrow(), (8, 'z')); +} + +#[test] +fn ref_mut_filter_map_accessor() { + struct X(RefCell>); + impl X { + fn accessor(&self) -> Option> { + RefMut::filter_map(self.0.borrow_mut(), |r| r.as_mut().ok()) + } + } + let x = X(RefCell::new(Ok(7))); + { + let mut d: RefMut = x.accessor().unwrap(); + assert_eq!(*d, 7); + *d += 1; + } + assert_eq!(*x.0.borrow(), Ok(8)); +} + #[test] fn as_unsafe_cell() { let c1: Cell = Cell::new(0); diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index 78c7215f550..3d14b3f3c81 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -24,6 +24,7 @@ #![feature(step_by)] #![feature(slice_patterns)] #![feature(float_from_str_radix)] +#![feature(cell_extras)] extern crate core; extern crate test;