Null ptr to external types?

The following does not compile:

#![feature(extern_types)]

extern "C" {
    type Test;
}

fn test() -> *const Test {
    std::ptr::null()
}

Playground

The fact is that Test is not Sized. I know that we have got fat pointers, so the general case can be tricky (here and here), but on the other hand we should be able to create a null pointer for an extern type like this without going unsafe.

Am I missing something?

1 Like

As a workaround you can 0usize as _ to create nullptr without unsafe code.

I'd use an Option type for this, and return None instead of a 'nullptr'.

Playground.

That is valid, but at the same time it is the code inside std::ptr::null.

If I need to avoid using std::ptr::null because my type is unsized, and the solution is writing the same code, maybe there is something wrong...

That is not the same thing. If you would like to obtain the same behavior, you should have used a Option<NonNull<Test>>, which is pretty cumbersome. Moreover, if you need to assign the null pointer to a struct with a pointer to Test, you cannot do that. It is possible to cast a 0usize to *const Test as said by @Hyeonu, but now we re-implemented ptr::null (see my previous reply).

1 Like

Typically I've seen C bindings to use some sort of dummy type instead of the type declaration you're using. E.g. gurobi-sys does this:

#[repr(C)]
struct Test {}

I know bindgen does something similar with some sort of array, but I can't find an example.

Yes, that's true. However, in this case the reason I wanted to use external_types is a bit more complex. I am trying to oxidize a C library, which exports many functions returning pointers to opaque types. In Rust we have some limitations because visibility is way better than C, but for these cases it can be troublesome.

The following example does not compile: playground.
The reason is that I cannot export a fn returning *const A if A is pub(crate).

I can use extern types feature to workaround the situation: defining an opaque extern type A, mod to choose between the real structure and the opaque type and cast between the pointers. Playground

You can imagine that, in this C-like codebase, I will need to use null and null_mut quite often. I can obviously copy-paste the std code in a module, add : ?Sized to the trait bound and go on, but it is not a solution I would like to adopt.

If the std is wrong, maybe it should be fixed. If it is right, I should not create a null pointer in that way.

That won't work, 0 as *mut _ only works for thin pointers, like Sized types and extern types, not fat pointers like slices and trait objects.

1 Like

Good point, I did not notice that I could not that. So, you are saying that it is not possible to create a generic function that can take Sized and extern types (which are unsized), but not fat pointers?

Exactly

1 Like

Crap. In this case I would need a crying face emoji in addition to the heart...

Actually, it looks like I am not the first one stumbling in this particular issue: