about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSimon Sapin <simon.sapin@exyr.org>2015-05-28 23:00:52 +0200
committerSimon Sapin <simon.sapin@exyr.org>2015-05-29 08:39:07 +0200
commitd0afa6ede3ce5fd6b35c8f1fd5fc89336ec2dc96 (patch)
tree5603388b531acbe6d1c72729a6e19057d66edc36
parentc516eee503ae643ead9553fed70528230feb2b1f (diff)
downloadrust-d0afa6ede3ce5fd6b35c8f1fd5fc89336ec2dc96.tar.gz
rust-d0afa6ede3ce5fd6b35c8f1fd5fc89336ec2dc96.zip
Add map and filter_map associated functions to std::cell::Ref and RefMut
See design discussion in https://github.com/rust-lang/rust/pull/25747
-rw-r--r--src/libcore/cell.rs133
-rw-r--r--src/libcoretest/cell.rs76
-rw-r--r--src/libcoretest/lib.rs1
3 files changed, 209 insertions, 1 deletions
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index dbb0db33366..9ad3ce11524 100644
--- a/src/libcore/cell.rs
+++ b/src/libcore/cell.rs
@@ -146,7 +146,7 @@ use clone::Clone;
 use cmp::PartialEq;
 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};
 
@@ -570,6 +570,137 @@ impl<'b, T: ?Sized> Ref<'b, T> {
             _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<u32> = Ref::map(b1, |t| &t.0);
+    /// assert_eq!(*b2, 5)
+    /// ```
+    #[unstable(feature = "cell_extras", reason = "recently added")]
+    #[inline]
+    pub fn map<U: ?Sized, F>(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<Result<u32, ()>> = c.borrow();
+    /// let b2: Ref<u32> = 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<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Option<Ref<'b, U>>
+        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<u32> = 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<U: ?Sized, F>(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<Result<u32, ()>> = c.borrow_mut();
+    ///     let mut b2: RefMut<u32> = 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<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> Option<RefMut<'b, U>>
+        where F: FnOnce(&mut T) -> Option<&mut U>
+    {
+        let RefMut { _value, _borrow } = orig;
+        f(_value).map(move |new| RefMut {
+            _value: new,
+            _borrow: _borrow,
+        })
+    }
 }
 
 struct BorrowRefMut<'b> {
diff --git a/src/libcoretest/cell.rs b/src/libcoretest/cell.rs
index eae8cd2c0f3..20740a5e2ce 100644
--- a/src/libcoretest/cell.rs
+++ b/src/libcoretest/cell.rs
@@ -130,6 +130,82 @@ fn ref_clone_updates_flag() {
 }
 
 #[test]
+fn ref_map_does_not_update_flag() {
+    let x = RefCell::new(Some(5));
+    {
+        let b1: Ref<Option<u32>> = x.borrow();
+        assert_eq!(x.borrow_state(), BorrowState::Reading);
+        {
+            let b2: Ref<u32> = 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<u32> {
+            Ref::map(self.0.borrow(), |tuple| &tuple.0)
+        }
+    }
+    let x = X(RefCell::new((7, 'z')));
+    let d: Ref<u32> = x.accessor();
+    assert_eq!(*d, 7);
+}
+
+#[test]
+fn ref_filter_map_accessor() {
+    struct X(RefCell<Result<u32, ()>>);
+    impl X {
+        fn accessor(&self) -> Option<Ref<u32>> {
+            Ref::filter_map(self.0.borrow(), |r| r.as_ref().ok())
+        }
+    }
+    let x = X(RefCell::new(Ok(7)));
+    let d: Ref<u32> = x.accessor().unwrap();
+    assert_eq!(*d, 7);
+}
+
+#[test]
+fn ref_mut_map_accessor() {
+    struct X(RefCell<(u32, char)>);
+    impl X {
+        fn accessor(&self) -> RefMut<u32> {
+            RefMut::map(self.0.borrow_mut(), |tuple| &mut tuple.0)
+        }
+    }
+    let x = X(RefCell::new((7, 'z')));
+    {
+        let mut d: RefMut<u32> = 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<Result<u32, ()>>);
+    impl X {
+        fn accessor(&self) -> Option<RefMut<u32>> {
+            RefMut::filter_map(self.0.borrow_mut(), |r| r.as_mut().ok())
+        }
+    }
+    let x = X(RefCell::new(Ok(7)));
+    {
+        let mut d: RefMut<u32> = x.accessor().unwrap();
+        assert_eq!(*d, 7);
+        *d += 1;
+    }
+    assert_eq!(*x.0.borrow(), Ok(8));
+}
+
+#[test]
 fn as_unsafe_cell() {
     let c1: Cell<usize> = Cell::new(0);
     c1.set(1);
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;