Should I Use NonNull<u8> or NonNull<()>?

I need to store type-erased pointers, and I've been using pointers to u8s, but I was curious whether or not there's a reason to use a raw pointer to a () instead. I think I've seen that before in a couple cases.

I kind of like the meaning that NonNull<()> implies, compared to NonNull<u8> which looks like it might intentionally point to a u8 while NonNull<()> seems to indicate it's pointing to something that you don't know what it is.

either is ok IMO because you typically keep the implementation private, and raw pointer always need unsafe to convert back, you just need to pay attention to those unsafe usages. if however you expose it in public APIs, I'd suggest use some custom opaque type (maybe uninhabited) instead:

pub enum Void {}
type Erased = NonNull<Void>;

or use the void crate.

1 Like

Oh, I like uninhabited idea, but I just found a page in the 'nomicon that said it was bad for FFI. I just found the c_void type in the standard library, and I'm wondering if that makes sense to use. I've got a couple functions in my API that I might want to be able to be implemented externally at some point so that might be a reason to use that instead.

well, I thought you were talking about rust only. if it's for ffi, then c_void is just for that. a pointer to c_void is equivalent to it's C counterpart. i.e. *const c_void => void const * and *mut c_void => void *. NonNull<c_void> is roughly the same as * mut c_void, but C doesn't have an equivalent "non-null" concept.

The RawWakerVTable API in the standard library uses *const ().

1 Like

Yeah, I hadn't thought about it until you mentioned void, and then I found the c_void, so I'll use that for now. Thanks!