Auto merge of #25747 - SimonSapin:map_ref, r=alexcrichton
For slightly complex data structures like `rustc_serialize::json::Json`, it is often convenient to have helper methods like `Json::as_string(&self) -> Option<&str>` that return a borrow of some component of `&self`. However, when `RefCell`s are involved, keeping a `Ref` around is required to hold a borrow to the insides of a `RefCell`. But `Ref` so far only references the entirety of the contents of a `RefCell`, not a component. But there is no reason it couldn’t: `Ref` internally contains just a data reference and a borrow count reference. The two can be dissociated. This adds a `map_ref` function that creates a new `Ref` for some other data, but borrowing the same `RefCell` as an existing `Ref`. Example: ```rust struct RefCellJson(RefCell<Json>); impl RefCellJson { fn as_string(&self) -> Option<Ref<str>> { map_ref(self.borrow(), |j| j.as_string()) } } ``` r? @alexcrichton
This commit is contained in:
commit
25fc917c65
@ -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<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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<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);
|
||||
|
@ -24,6 +24,7 @@
|
||||
#![feature(step_by)]
|
||||
#![feature(slice_patterns)]
|
||||
#![feature(float_from_str_radix)]
|
||||
#![feature(cell_extras)]
|
||||
|
||||
extern crate core;
|
||||
extern crate test;
|
||||
|
Loading…
Reference in New Issue
Block a user