Enabling pointer space optimization for Option<MyType>



To be more precise, I’m having a type which contains some pointer value. What do I need to do to enable space optimization for my type, so an Option will be of the same size as MyType, with None represented as nullptr?



Wrap the pointer in Unique. It’s not being stabilized any time soon though.


Bad news. I’m targeting stable.


If you use one of the built-in pointer types, like Box, Rc, & or &mut, you get this automatically.

use std::mem;
struct Foo {
    x: Box<u8>

fn main() {
    println!("Foo {}\nOption<Foo> {}",
Foo 8
Option<Foo> 8

If you’re using raw pointers, then the unstable Shared and Unique types are the best route. (Of course, the best route is to use the safe pointers, rather than raw pointers.)


Unfortunately, I need raw pointer there. Because that thing is in fact an opaque handle. So I think I’m out of luck.


You can probably get away with transmuting it to something like &u8 and storing it in a private field, but in theory you shouldn’t because it is considered undefined behavior.


Is it actually undefined behavior to transmute a *const T to a &T as long as you ensure the pointed-to value is valid for the lifetime of the reference? (And don’t violate the aliasing rules.) I don’t see what that violates in the UB list, and the standard library does something very similar.


Well, it depends on whether the “opaque handle” is actually a pointer or not. Chances are it is, but…

edit: Never mind, derp, I probably misunderstood the OP. I thought they were referring to something like a uintptr_t as the opaque handle (with 0 guaranteed to be invalid), but struct forward_declared * would make more sense, in which case transmuting to &u8 should be fine.


Should it be, though? I’ve been entertaining thoughts that, since a &u8 is statically guaranteed to not dangle and not change, it could be eagerly dereferenced (thus having size 1 and not being transmute-compatible with either &mut u8 or *const u8, both of which must be pointers). This is much more interesting as an optimization for cases like &Rc<Foo>; we don’t need to make a pointer to a pointer, since Rc does not contain an UnsafeCell, we can “borrow” by copying.


To be precise it’s a pointer to top of coroutine stack. It participates in coroutine enter/leave cycle.


Look at NonZero.
It might be just what you need.



Making &u8 size 1 would make it impossible to compile the valid safe expression some_u8_ref as *const u8. (Although I think a &() or &fn() would be more idiomatic for this case anyway.)