Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add std::cell::Ref::map and friends #25747

Merged
merged 2 commits into from
May 29, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 152 additions & 4 deletions src/libcore/cell.rs
Original file line number Diff line number Diff line change
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 @@ -545,13 +545,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<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,
})
}
}

Expand Down
80 changes: 78 additions & 2 deletions src/libcoretest/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,20 +115,96 @@ 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);
}
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
Original file line number Diff line number Diff line change
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