Skip to content

Commit

Permalink
Add std::cell::map_ref
Browse files Browse the repository at this point in the history
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>> {
        let borrow = self.borrow();
        borrow.as_string().map(|s| map_ref(&borrow, s))
    }
}
```
  • Loading branch information
SimonSapin committed May 24, 2015
1 parent 62d4b62 commit 62e8200
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 0 deletions.
27 changes: 27 additions & 0 deletions src/libcore/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,33 @@ pub fn clone_ref<'b, T: ?Sized>(orig: &Ref<'b, T>) -> Ref<'b, T> {
}
}

/// Make a new `Ref` for a component of the borrowed data, e.g. an enum variant.
///
/// The `RefCell` is already immutably borrowed, so this cannot fail.
///
/// A method would interfere with methods of the same name on the contents of a `RefCell`
/// used through `Deref`.
///
/// # Example
///
/// ```
/// use std::cell::{RefCell, Ref, map_ref};
///
/// let c = RefCell::new(Some(5));
/// let b1: Ref<Option<u32>> = c.borrow();
/// let b2: Ref<u32> = map_ref(&b1, (*b1).as_ref().unwrap());
/// assert_eq!(*b2, 5);
/// ```
#[unstable(feature = "core",
reason = "recently added")]
#[inline]
pub fn map_ref<'b, T: ?Sized, U: ?Sized>(orig: &Ref<'b, T>, new: &'b U) -> Ref<'b, U> {
Ref {
_value: new,
_borrow: orig._borrow.clone(),
}
}

struct BorrowRefMut<'b> {
_borrow: &'b Cell<BorrowFlag>,
}
Expand Down
16 changes: 16 additions & 0 deletions src/libcoretest/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ fn clone_ref_updates_flag() {
assert_eq!(x.borrow_state(), BorrowState::Unused);
}

#[test]
fn map_ref_updates_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> = map_ref(&b1, b1.as_ref().unwrap());
assert_eq!(*b2, 5);
assert_eq!(x.borrow_state(), BorrowState::Reading);
}
assert_eq!(x.borrow_state(), BorrowState::Reading);
}
assert_eq!(x.borrow_state(), BorrowState::Unused);
}

#[test]
fn as_unsafe_cell() {
let c1: Cell<usize> = Cell::new(0);
Expand Down

0 comments on commit 62e8200

Please sign in to comment.