Expand description
Functions for semi-safely casting various things to each other.
We mostly use this for slices of geometry and instance data, since you want
to be able to write simple Rust structs to manage your shader data, which
we then read out as f32 slices or Vecs.
Given a struct like
#[repr(C)]
struct MyStruct(u32, u32);the following casts are supported and safe:
cast_slice::<MyStruct, u32>(..)cast_slice::<u32, MyStruct>(..)if the slice has the rightlencast_slice::<MyStruct, u8>(..)cast_vec::<MyStruct, u32>(..)cast_vec::<u32, MyStruct>(..)if the Vec has the rightlenandcapacity
The following casts cause a panic:
cast_slice::<u8, MyStruct>(..)“from” alignment is not a multiple of “to” alignmentcast_slice::<MyStruct, u64>(..)“from” alignment is not a multiple of “to” alignmentcast_vec::<MyStruct, u64>(..)“from” alignment is different from “to” alignmentcast_vec::<MyStruct, u8>(..)“from” alignment is different from “to” alignment
The following casts are unsafe but DON’T cause a panic. Please just don’t do them.
cast_slice::<u32, Option<u32>>or any other data structure that can’t be arbitrarily initializedcast_slice::<u32, (u32, u32)>doesn’t use#[repr(C)], so might behave unexpectedlycast_vecwith a custom allocator (requires the unstable allocator_api feature)
TODO(JP): We should check that you’re casting to a struct that can be initialized with
arbitrary byte data. Ideally Rust should implement something like an
Arbitrary trait.
We currently use 'static + Copy as a proxy for such a trait, since at least this disallows
anything that implements Drop or has (non-static) references. We should also update
places like crate::Area::get_slice when improving this.
Even better could be to add a custom derive macro to make sure that a struct only contains
a certain type of scalar, e.g. #[derive(OnlyScalars<f32>)] for a typical geometry or instance
struct. Then we can prevent a bunch of runtime panics at compile time.
We can also use something like https://docs.rs/repr-trait/latest/repr_trait/trait.C.html
to make sure the data is properly represented using #[repr(C)].
See:
- https://users.rust-lang.org/t/why-does-vec-from-raw-parts-require-same-size-and-not-same-size-capacity/73036
- https://github.com/nabijaczleweli/safe-transmute-rs/issues/16#issuecomment-471066699
- https://rawcdn.githack.com/nabijaczleweli/safe-transmute-rs/doc/safe_transmute/fn.transmute_vec.html
- https://github.com/rust-lang/rust/blob/190feb65290d39d7ab6d44e994bd99188d339f16/src/libstd/sys/windows/alloc.rs#L46-L57
- https://github.com/rust-lang/rust/blob/b6f580acc0ce233d5c4d1f9680d354fded88b824/library/std/src/sys/common/alloc.rs#L30
Functions
Cast a slice from one type to another.