Skip to content

Commit 2b49fc6

Browse files
committed
Add std::cell::map_ref
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>> { make_ref(self.borrow(), |j| j.as_string()) } } ```
1 parent 62d4b62 commit 2b49fc6

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

src/libcore/cell.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,33 @@ pub fn clone_ref<'b, T: ?Sized>(orig: &Ref<'b, T>) -> Ref<'b, T> {
555555
}
556556
}
557557

558+
/// Make a new `Ref` for a component of the borrowed data, e.g. an enum variant.
559+
///
560+
/// The `RefCell` is already immutably borrowed, so this cannot fail.
561+
///
562+
/// A method would interfere with methods of the same name on the contents of a `RefCell`
563+
/// used through `Deref`.
564+
///
565+
/// # Example
566+
///
567+
/// ```
568+
/// use std::cell::{RefCell, Ref, map_ref};
569+
///
570+
/// let c = RefCell::new(Some(5));
571+
/// let b1: Ref<Option<u32>> = c.borrow();
572+
/// let b2: Ref<u32> = map_ref(&b1, (*b1).as_ref().unwrap());
573+
/// assert_eq!(*b2, 5);
574+
/// ```
575+
#[unstable(feature = "core",
576+
reason = "recently added")]
577+
#[inline]
578+
pub fn map_ref<'b, T: ?Sized, U: ?Sized>(orig: &Ref<'b, T>, new: &'b U) -> Ref<'b, U> {
579+
Ref {
580+
_value: new,
581+
_borrow: orig._borrow.clone(),
582+
}
583+
}
584+
558585
struct BorrowRefMut<'b> {
559586
_borrow: &'b Cell<BorrowFlag>,
560587
}

src/libcoretest/cell.rs

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

132+
#[test]
133+
fn map_ref_updates_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> = map_ref(&b1, b1.as_ref().unwrap());
140+
assert_eq!(*b2, 5);
141+
assert_eq!(x.borrow_state(), BorrowState::Reading);
142+
}
143+
assert_eq!(x.borrow_state(), BorrowState::Reading);
144+
}
145+
assert_eq!(x.borrow_state(), BorrowState::Unused);
146+
}
147+
132148
#[test]
133149
fn as_unsafe_cell() {
134150
let c1: Cell<usize> = Cell::new(0);

0 commit comments

Comments
 (0)