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.