Skip to content

Commit d0afa6e

Browse files
committed
Add map and filter_map associated functions to std::cell::Ref and RefMut
See design discussion in #25747
1 parent c516eee commit d0afa6e

File tree

3 files changed

+209
-1
lines changed

3 files changed

+209
-1
lines changed

src/libcore/cell.rs

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ use clone::Clone;
146146
use cmp::PartialEq;
147147
use default::Default;
148148
use marker::{Copy, Send, Sync, Sized};
149-
use ops::{Deref, DerefMut, Drop};
149+
use ops::{Deref, DerefMut, Drop, FnOnce};
150150
use option::Option;
151151
use option::Option::{None, Some};
152152

@@ -570,6 +570,137 @@ impl<'b, T: ?Sized> Ref<'b, T> {
570570
_borrow: orig._borrow.clone(),
571571
}
572572
}
573+
574+
/// Make a new `Ref` for a component of the borrowed data.
575+
///
576+
/// The `RefCell` is already immutably borrowed, so this cannot fail.
577+
///
578+
/// This is an associated function that needs to be used as `Ref::map(...)`.
579+
/// A method would interfere with methods of the same name on the contents of a `RefCell`
580+
/// used through `Deref`.
581+
///
582+
/// # Example
583+
///
584+
/// ```
585+
/// # #![feature(cell_extras)]
586+
/// use std::cell::{RefCell, Ref};
587+
///
588+
/// let c = RefCell::new((5, 'b'));
589+
/// let b1: Ref<(u32, char)> = c.borrow();
590+
/// let b2: Ref<u32> = Ref::map(b1, |t| &t.0);
591+
/// assert_eq!(*b2, 5)
592+
/// ```
593+
#[unstable(feature = "cell_extras", reason = "recently added")]
594+
#[inline]
595+
pub fn map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Ref<'b, U>
596+
where F: FnOnce(&T) -> &U
597+
{
598+
Ref {
599+
_value: f(orig._value),
600+
_borrow: orig._borrow,
601+
}
602+
}
603+
604+
/// Make a new `Ref` for a optional component of the borrowed data, e.g. an enum variant.
605+
///
606+
/// The `RefCell` is already immutably borrowed, so this cannot fail.
607+
///
608+
/// This is an associated function that needs to be used as `Ref::filter_map(...)`.
609+
/// A method would interfere with methods of the same name on the contents of a `RefCell`
610+
/// used through `Deref`.
611+
///
612+
/// # Example
613+
///
614+
/// ```
615+
/// # #![feature(cell_extras)]
616+
/// use std::cell::{RefCell, Ref};
617+
///
618+
/// let c = RefCell::new(Ok(5));
619+
/// let b1: Ref<Result<u32, ()>> = c.borrow();
620+
/// let b2: Ref<u32> = Ref::filter_map(b1, |o| o.as_ref().ok()).unwrap();
621+
/// assert_eq!(*b2, 5)
622+
/// ```
623+
#[unstable(feature = "cell_extras", reason = "recently added")]
624+
#[inline]
625+
pub fn filter_map<U: ?Sized, F>(orig: Ref<'b, T>, f: F) -> Option<Ref<'b, U>>
626+
where F: FnOnce(&T) -> Option<&U>
627+
{
628+
f(orig._value).map(move |new| Ref {
629+
_value: new,
630+
_borrow: orig._borrow,
631+
})
632+
}
633+
}
634+
635+
impl<'b, T: ?Sized> RefMut<'b, T> {
636+
/// Make a new `RefMut` for a component of the borrowed data, e.g. an enum variant.
637+
///
638+
/// The `RefCell` is already mutably borrowed, so this cannot fail.
639+
///
640+
/// This is an associated function that needs to be used as `RefMut::map(...)`.
641+
/// A method would interfere with methods of the same name on the contents of a `RefCell`
642+
/// used through `Deref`.
643+
///
644+
/// # Example
645+
///
646+
/// ```
647+
/// # #![feature(cell_extras)]
648+
/// use std::cell::{RefCell, RefMut};
649+
///
650+
/// let c = RefCell::new((5, 'b'));
651+
/// {
652+
/// let b1: RefMut<(u32, char)> = c.borrow_mut();
653+
/// let mut b2: RefMut<u32> = RefMut::map(b1, |t| &mut t.0);
654+
/// assert_eq!(*b2, 5);
655+
/// *b2 = 42;
656+
/// }
657+
/// assert_eq!(*c.borrow(), (42, 'b'));
658+
/// ```
659+
#[unstable(feature = "cell_extras", reason = "recently added")]
660+
#[inline]
661+
pub fn map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> RefMut<'b, U>
662+
where F: FnOnce(&mut T) -> &mut U
663+
{
664+
RefMut {
665+
_value: f(orig._value),
666+
_borrow: orig._borrow,
667+
}
668+
}
669+
670+
/// Make a new `RefMut` for a optional component of the borrowed data, e.g. an enum variant.
671+
///
672+
/// The `RefCell` is already mutably borrowed, so this cannot fail.
673+
///
674+
/// This is an associated function that needs to be used as `RefMut::filter_map(...)`.
675+
/// A method would interfere with methods of the same name on the contents of a `RefCell`
676+
/// used through `Deref`.
677+
///
678+
/// # Example
679+
///
680+
/// ```
681+
/// # #![feature(cell_extras)]
682+
/// use std::cell::{RefCell, RefMut};
683+
///
684+
/// let c = RefCell::new(Ok(5));
685+
/// {
686+
/// let b1: RefMut<Result<u32, ()>> = c.borrow_mut();
687+
/// let mut b2: RefMut<u32> = RefMut::filter_map(b1, |o| o.as_mut().ok()).unwrap();
688+
/// assert_eq!(*b2, 5);
689+
/// *b2 = 42;
690+
/// }
691+
/// assert_eq!(*c.borrow(), Ok(42));
692+
/// ```
693+
#[unstable(feature = "cell_extras", reason = "recently added")]
694+
#[inline]
695+
pub fn filter_map<U: ?Sized, F>(orig: RefMut<'b, T>, f: F) -> Option<RefMut<'b, U>>
696+
where F: FnOnce(&mut T) -> Option<&mut U>
697+
{
698+
let RefMut { _value, _borrow } = orig;
699+
f(_value).map(move |new| RefMut {
700+
_value: new,
701+
_borrow: _borrow,
702+
})
703+
}
573704
}
574705

575706
struct BorrowRefMut<'b> {

src/libcoretest/cell.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,82 @@ fn ref_clone_updates_flag() {
129129
assert_eq!(x.borrow_state(), BorrowState::Unused);
130130
}
131131

132+
#[test]
133+
fn ref_map_does_not_update_flag() {
134+
let x = RefCell::new(Some(5));
135+
{
136+
let b1: Ref<Option<u32>> = x.borrow();
137+
assert_eq!(x.borrow_state(), BorrowState::Reading);
138+
{
139+
let b2: Ref<u32> = Ref::map(b1, |o| o.as_ref().unwrap());
140+
assert_eq!(*b2, 5);
141+
assert_eq!(x.borrow_state(), BorrowState::Reading);
142+
}
143+
assert_eq!(x.borrow_state(), BorrowState::Unused);
144+
}
145+
assert_eq!(x.borrow_state(), BorrowState::Unused);
146+
}
147+
148+
#[test]
149+
fn ref_map_accessor() {
150+
struct X(RefCell<(u32, char)>);
151+
impl X {
152+
fn accessor(&self) -> Ref<u32> {
153+
Ref::map(self.0.borrow(), |tuple| &tuple.0)
154+
}
155+
}
156+
let x = X(RefCell::new((7, 'z')));
157+
let d: Ref<u32> = x.accessor();
158+
assert_eq!(*d, 7);
159+
}
160+
161+
#[test]
162+
fn ref_filter_map_accessor() {
163+
struct X(RefCell<Result<u32, ()>>);
164+
impl X {
165+
fn accessor(&self) -> Option<Ref<u32>> {
166+
Ref::filter_map(self.0.borrow(), |r| r.as_ref().ok())
167+
}
168+
}
169+
let x = X(RefCell::new(Ok(7)));
170+
let d: Ref<u32> = x.accessor().unwrap();
171+
assert_eq!(*d, 7);
172+
}
173+
174+
#[test]
175+
fn ref_mut_map_accessor() {
176+
struct X(RefCell<(u32, char)>);
177+
impl X {
178+
fn accessor(&self) -> RefMut<u32> {
179+
RefMut::map(self.0.borrow_mut(), |tuple| &mut tuple.0)
180+
}
181+
}
182+
let x = X(RefCell::new((7, 'z')));
183+
{
184+
let mut d: RefMut<u32> = x.accessor();
185+
assert_eq!(*d, 7);
186+
*d += 1;
187+
}
188+
assert_eq!(*x.0.borrow(), (8, 'z'));
189+
}
190+
191+
#[test]
192+
fn ref_mut_filter_map_accessor() {
193+
struct X(RefCell<Result<u32, ()>>);
194+
impl X {
195+
fn accessor(&self) -> Option<RefMut<u32>> {
196+
RefMut::filter_map(self.0.borrow_mut(), |r| r.as_mut().ok())
197+
}
198+
}
199+
let x = X(RefCell::new(Ok(7)));
200+
{
201+
let mut d: RefMut<u32> = x.accessor().unwrap();
202+
assert_eq!(*d, 7);
203+
*d += 1;
204+
}
205+
assert_eq!(*x.0.borrow(), Ok(8));
206+
}
207+
132208
#[test]
133209
fn as_unsafe_cell() {
134210
let c1: Cell<usize> = Cell::new(0);

src/libcoretest/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#![feature(step_by)]
2525
#![feature(slice_patterns)]
2626
#![feature(float_from_str_radix)]
27+
#![feature(cell_extras)]
2728

2829
extern crate core;
2930
extern crate test;

0 commit comments

Comments
 (0)