Enabling pointer space optimization for Option<MyType>


#1

Hi,

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?

Thanks


#2

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


#3

Bad news. I’m targeting stable.


#4

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> {}",
             mem::size_of::<Foo>(),
             mem::size_of::<Option<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.)


#5

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


#6

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.


#7

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.


#8

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.


#9

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.


#10

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


#11

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


#12

#13

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