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.
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).
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.
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?