Skip to content

Commit

Permalink
Add map and filter_map associated functions to std::cell::Ref and RefMut
Browse files Browse the repository at this point in the history
See design discussion in #25747
  • Loading branch information
SimonSapin committed May 29, 2015
1 parent c516eee commit d0afa6e
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 1 deletion.
133 changes: 132 additions & 1 deletion src/libcore/cell.rs
Expand Up @@ -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};

Expand Down Expand Up @@ -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> {
Expand Down
76 changes: 76 additions & 0 deletions src/libcoretest/cell.rs
Expand Up @@ -129,6 +129,82 @@ fn ref_clone_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<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);
Expand Down
1 change: 1 addition & 0 deletions src/libcoretest/lib.rs
Expand Up @@ -24,6 +24,7 @@
#![feature(step_by)]
#![feature(slice_patterns)]
#![feature(float_from_str_radix)]
#![feature(cell_extras)]

extern crate core;
extern crate test;
Expand Down

0 comments on commit d0afa6e

Please sign in to comment.