Hi!
I have a simple bare struct with a simple interface:
pub struct Context(*const ());
impl Context {
pub fn empty() -> Context { Context(ptr::null()) }
pub unsafe fn new(stack_base: *mut (), stack_size: usize, func: extern fn(isize, usize) -> !, param: usize) -> Context {
// impl omitted
}
pub fn is_empty(&self) -> bool { self.0 == ptr::null() }
pub fn jump(&mut self, store: &mut Context, message: isize) -> isize {
// impl omitted
}
pub fn jump_into(&mut self, message: isize) -> ! {
// impl omitted
}
}
The semantics for jump*
functions tell that self
should be non-empty and will be nullified during call. I.e. Context
will be consumed. Also, store
parameter to jump
cannot be returned as return value, should be nullptr
when used as parameter, and will be initialized by func.
The whole Context
is designed as stale storage cell, which doesn't change its location and is often modified from initialized to empty and back.
My question is, whether this API design is mostly Ok. I see three alternatives:
- Leave as-is
- Impl for Option, and declare nullability this way
- Replace
self
references with owned values. The problem is, it will clutter user code heavily with things likemem::replace
, because storage itself will reside in the same piece of memory all the time.
Thanks!