1 //! This module provides a way to deal with self-referential data.
3 //! The main idea is to allocate such data in a generator frame and then
4 //! give access to it by executing user-provided closures inside that generator.
5 //! The module provides a safe abstraction for the latter task.
7 //! The interface consists of two exported macros meant to be used together:
8 //! * `declare_box_region_type` wraps a generator inside a struct with `access`
9 //! method which accepts closures.
10 //! * `box_region_allow_access` is a helper which should be called inside
11 //! a generator to actually execute those closures.
13 use std
::marker
::PhantomData
;
14 use std
::ops
::{Generator, GeneratorState}
;
17 #[derive(Copy, Clone)]
18 pub struct AccessAction(*mut dyn FnMut());
21 pub fn get(self) -> *mut dyn FnMut() {
26 #[derive(Copy, Clone)]
33 pub struct PinnedGenerator
<I
, A
, R
> {
34 generator
: Pin
<Box
<dyn Generator
<Action
, Yield
= YieldType
<I
, A
>, Return
= R
>>>,
37 impl<I
, A
, R
> PinnedGenerator
<I
, A
, R
> {
38 pub fn new
<T
: Generator
<Action
, Yield
= YieldType
<I
, A
>, Return
= R
> + '
static>(
41 let mut result
= PinnedGenerator { generator: Box::pin(generator) }
;
43 // Run it to the first yield to set it up
44 let init
= match Pin
::new(&mut result
.generator
).resume(Action
::Initial
) {
45 GeneratorState
::Yielded(YieldType
::Initial(y
)) => y
,
52 pub unsafe fn access(&mut self, closure
: *mut dyn FnMut()) {
53 // Call the generator, which in turn will call the closure
54 if let GeneratorState
::Complete(_
) =
55 Pin
::new(&mut self.generator
).resume(Action
::Access(AccessAction(closure
)))
61 pub fn complete(&mut self) -> R
{
62 // Tell the generator we want it to complete, consuming it and yielding a result
63 let result
= Pin
::new(&mut self.generator
).resume(Action
::Complete
);
64 if let GeneratorState
::Complete(r
) = result { r }
else { panic!() }
69 pub struct Marker
<T
>(PhantomData
<T
>);
72 pub unsafe fn new() -> Self {
77 pub enum YieldType
<I
, A
> {
83 #[allow_internal_unstable(fn_traits)]
84 macro_rules
! declare_box_region_type
{
88 for($
($lifetimes
:tt
)*),
89 ($
($args
:ty
),*) -> ($reti
:ty
, $retc
:ty
)
91 $v
struct $
name($
crate::box_region
::PinnedGenerator
<
93 for<$
($lifetimes
)*> fn(($
($args
,)*)),
98 fn new
<T
: ::std
::ops
::Generator
<$
crate::box_region
::Action
, Yield
= $yield_type
, Return
= $retc
> + '
static>(
101 let (initial
, pinned
) = $
crate::box_region
::PinnedGenerator
::new(generator
);
102 (initial
, $
name(pinned
))
105 $v
fn access
<F
: for<$
($lifetimes
)*> FnOnce($
($args
,)*) -> R
, R
>(&mut self, f
: F
) -> R
{
106 // Turn the FnOnce closure into *mut dyn FnMut()
107 // so we can pass it in to the generator
110 let mut_f
: &mut dyn for<$
($lifetimes
)*> FnMut(($
($args
,)*)) =
112 let f
= f
.take().unwrap();
113 r
= Some(FnOnce
::call_once(f
, args
));
115 let mut_f
= mut_f
as *mut dyn for<$
($lifetimes
)*> FnMut(($
($args
,)*));
117 // Get the generator to call our closure
119 self.0.access(::std
::mem
::transmute(mut_f
));
126 $v
fn complete(mut self) -> $retc
{
130 fn initial_yield(value
: $reti
) -> $yield_type
{
131 $
crate::box_region
::YieldType
::Initial(value
)
136 ($v
:vis $name
: ident
, for($
($lifetimes
:tt
)*), ($
($args
:ty
),*) -> ($reti
:ty
, $retc
:ty
)) => {
137 declare_box_region_type
!(
139 $
crate::box_region
::YieldType
<$reti
, for<$
($lifetimes
)*> fn(($
($args
,)*))>,
141 ($
($args
),*) -> ($reti
, $retc
)
147 #[allow_internal_unstable(fn_traits)]
148 macro_rules
! box_region_allow_access
{
149 (for($
($lifetimes
:tt
)*), ($
($args
:ty
),*), ($
($exprs
:expr
),*), $action
:ident
) => {
152 $
crate::box_region
::Action
::Access(accessor
) => {
153 let accessor
: &mut dyn for<$
($lifetimes
)*> FnMut($
($args
),*) = unsafe {
154 ::std
::mem
::transmute(accessor
.get())
156 (*accessor
)(($
($exprs
),*));
158 let marker
= $
crate::box_region
::Marker
::<
159 for<$
($lifetimes
)*> fn(($
($args
,)*))
161 $action
= yield $
crate::box_region
::YieldType
::Accessor(marker
);
164 $
crate::box_region
::Action
::Complete
=> break,
165 $
crate::box_region
::Action
::Initial
=> panic
!("unexpected box_region action: Initial"),