Skip to content

Commit 25fc917

Browse files
committed
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
2 parents 42a59ae + d0afa6e commit 25fc917

File tree

3 files changed

+231
-6
lines changed

3 files changed

+231
-6
lines changed

src/libcore/cell.rs

Lines changed: 152 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ use clone::Clone;
146146
use cmp::{PartialEq, Eq};
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

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

src/libcoretest/cell.rs

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,20 +115,96 @@ fn discard_doesnt_unborrow() {
115115
}
116116

117117
#[test]
118-
fn clone_ref_updates_flag() {
118+
fn ref_clone_updates_flag() {
119119
let x = RefCell::new(0);
120120
{
121121
let b1 = x.borrow();
122122
assert_eq!(x.borrow_state(), BorrowState::Reading);
123123
{
124-
let _b2 = clone_ref(&b1);
124+
let _b2 = Ref::clone(&b1);
125125
assert_eq!(x.borrow_state(), BorrowState::Reading);
126126
}
127127
assert_eq!(x.borrow_state(), BorrowState::Reading);
128128
}
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)