Closed LawrenceEsswood closed 7 months ago
The reason the types are wrapped in boxes is because of compiler optimizations that go with Option<T>
. For example, bool
and u8
have the same size, but Option<bool>
is 1 byte and Option<u8>
is 2 bytes. (From testing with compiler explorer, Option<bool>
uses 2 to indicate None
).
With an implementation without Box
in DummyEnum
, doing cve_rs::transmute::<bool, u8>(true)
panics with called `Option::unwrap()` on a `None` value
, while std::mem::transmute::<bool, u8>(true)
gives 1
. This is bad, because we don't want our memory-safe memory unsafeties to panic.
If there is a way to work around this, please let us know in another comment/issue/pull request! Closing for now as this implementation introduces bugs.
Ah, I see!
I think the correct solution is repr(C) as I think that guarantees the "tagged union" representation of an enum. This ensures your option is a tag followed by value, but then you then have to make sure it "follows" at the same offset.
The solution to that is to wrap again with a struct that contains a zero-length array of the other type so that the alignment of the two wrappers is the same:
https://godbolt.org/z/MKz93zjfK
Embedded systems could really do with memory-safe memory unsafety.
That's genius! I was working on a new implementation of transmute that exploits a different known compiler bug, but couldn't get it working because of misaligned options. It now works and reliably outperforms the standard library for some reason! xD
Will hopefully PR this soon.
Note: These benchmarks don't include the changes from #26 - that might make the current implementation faster than these benches show as well.
I wonder if it could also be achieved using a Cell
, as UnsafeCell
is excluded from niche optimizations, as it introduces a region of mutable sharable memory, which would interfere with the containing type. (MaybeUnit
also prevents layout optimizations, as it is an union
and may contain uninitialized data (as in the name), but getting the data out may not be possible).
Seems to work fine without Box as both types are sized (which requires alloc):