Hello all,
I’m struggling with an API design issue that is slightly above my level of experience. I would be so grateful for your help.
The problem is very simple: provide an easy to use Rusty handle to a piece of data managed as a void pointer in a C library. A little graphic of my current design:
Bindings API | "DataHandle" | C library
| |
provides +---------+ manages
Ref<T> <------+----| RefCell |-----+---> *mut c_void
RefMut<T> <------+----| *mut T | |
T | +---------+ |
... | |
struct DataHandle<T> {
data_ptr: RefCell<Option<NonNull<T>>>,
}
impl<T> DataHandle<T> {
fn replace(&self, data: T) -> Result<Option<T>>;
fn take(&self) -> Result<Option<T>>;
fn borrow(&self) -> Result<Option<Ref<T>>>;
fn borrow_mut(&self) -> Result<Option<RefMut<T>>>;
}
This is my DataHandle
. It works, it’s usable. Fair enough!
I do have a feeling that it could be better than this. Couldn’t I make an API that uses actual Rust references (&
/&mut
) instead of the RefCell
? So that I get the usual compile-time aliasing/safety guarantees? But then I need a different kind of interior mutability – UnsafeCell
, probably.
I have a first sketch of what that would look like, but it uses a lot of unsafe
and there’s even a worrying clippy error (mutable borrow from immutable input(s)
). I’ve also made it lazy, not at all sure it’s done right.
impl<T> DataHandle<T> {
fn replace(&mut self, data: T) -> Result<Option<T>>;
fn take(&mut self) -> Result<Option<T>>;
fn borrow(&self) -> Option<&T>;
fn borrow_mut(&mut self) -> Option<&mut T>>;
}
Here is the sketch.
My most pressing question is whether this contains UB.
More generally: thoughts on this design problem? Other approaches how a DataHandle
(just access to data behind a raw pointer!) could be done, is done?
I know it’s a lot to ask to review this, but this is definitely one of those cases where a fresh (and more capable) pair of eyes can see better … thank you!