mucell::mucell_ref_type!
[-]
[+]
[src]
macro_rules! mucell_ref_type { ( $(#[$attr:meta])* // suggestions: doc, stability markers struct $ref_name:ident<'a>($ty:ty), impl Deref -> $deref:ty, data: $data_ty:ty = |$data_ident:ident| $data_expr:expr ) => { /// An immutable reference to a `MuCell`. Dereference to get at the object. $(#[$attr])* pub struct $ref_name<'a> { _parent: Ref<'a, $ty>, _data: $data_ty, } #[unstable = "still a little experimental, like the whole macro"] impl<'a> $ref_name<'a> { /// Construct a reference from the cell. #[unstable = "still a little experimental, like the whole macro"] fn from(cell: &'a MuCell<$ty>) -> $ref_name<'a> { let parent = cell.borrow(); // This transmutation is to fix the lifetime of the reference so it is 'a rather // than the block. Because we keep the parent around in the struct, it’ll be OK; // even if the parent destructor is run before any data destructor and it does // silly things with any references, because we’re not Sync it doesn’t matter if // `borrows` is decremented early. We could just use `transmute(&*parent)` here, // but for a macro it’s nice to avoid depending on std or core being in a // particular place is of value. (Caring about efficiency? Unoptimised, this way is // slightly less efficient, optimised both are noops.) let $data_ident: &'a $ty = unsafe { &*(&*parent as *const $ty) }; let data = $data_expr; $ref_name { _parent: parent, _data: data, } } } #[unstable = "trait is not stable"] impl<'a> ::std::ops::Deref for $ref_name<'a> { type Target = $deref; fn deref<'b>(&'b self) -> &'b $deref { &*self._data } } } }
Create a new reference type to something inside the cell.
Why is this necesary? Because of the tracking of immutable references (Ref<'a, T>
rather than
&'a T
), anything from the object owning the original cell wishing to return a reference to
something inside the cell must go producing another such reference type like Ref
, with its
own Deref
implementation and so forth. (This is the cost of efficient memory safety!)
Here’s an example of usage:
#[macro_use] extern crate mucell; use mucell::{MuCell, Ref}; struct Foo { bar: String, } mucell_ref_type! { #[doc = "…"] struct BarRef<'a>(Foo), impl Deref -> str, data: &'a str = |x| x.bar.as_slice() } fn pull_string_out(foo: &MuCell<Foo>) -> BarRef { // Maybe pretend we did something like `try_mutate` here. // We would not be able to return foo.borrow().bar.as_slice() // here because the borrow() lifetime would be too short. // So we use our fancy new ref type! BarRef::from(foo) } fn say(s: &str) { println!("The string is “{}”", s); } fn demo(foo: &MuCell<Foo>) { say(&*pull_string_out(foo)); } fn main() { demo(&MuCell::new(Foo { bar: format!("panic") })); }
The vector_munger
example demonstrates a more complex use case.