pub unsafe trait ArrayCast: Sized {
type Array: ArrayExt;
}
Expand description
Marker trait for types that can be represented as a fixed size array.
A type that implements this trait is assumed to have the exact same memory layout and representation as a fixed size array. This implies a couple of useful properties:
- Casting between
T
andT::Array
is free and will (or should) be optimized away. [T]
can be cast to and from[T::Array]
, which can be cast to and from[U]
whereT::Array = [U; N]
and the length is a multiple ofN
.
This allows a number of common and useful optimizations, including casting
buffers and reusing memory. It does however come with some strict
requirements and the recommendation is to use #[derive(ArrayCast)]
which
checks for them automatically.
§Deriving
ArrayCast
can be automatically derived. The only requirements are that the
type is a struct
, that it has a #[repr(C)]
or #[repr(transparent)]
attribute, and that all of its fields have the same types. It stays on the
conservative side and will show an error if any of those requirements are
not fulfilled. If some fields have different types, but the same memory
layout, or are zero-sized, they can be marked with attributes to show that
their types are safe to use.
§Field Attributes
-
#[palette_unsafe_same_layout_as = "SomeType"]
: Mark the field as having the same memory layout asSomeType
.Safety: corrupt data and undefined behavior may occur if this is not true!
-
#[palette_unsafe_zero_sized]
: Mark the field as being zero-sized, and thus not taking up any memory space. This means that it can be ignored.Safety: corrupt data and undefined behavior may occur if this is not true!
§Examples
Basic use:
use palette::cast::{self, ArrayCast};
#[derive(PartialEq, Debug, ArrayCast)]
#[repr(C)]
struct MyCmyk {
cyan: f32,
magenta: f32,
yellow: f32,
key: f32,
}
let buffer = [0.1, 0.2, 0.3, 0.4];
let color: MyCmyk = cast::from_array(buffer);
assert_eq!(
color,
MyCmyk {
cyan: 0.1,
magenta: 0.2,
yellow: 0.3,
key: 0.4,
}
);
Heterogenous field types:
use std::marker::PhantomData;
use palette::{cast::{self, ArrayCast}, encoding::Srgb, RgbHue};
#[derive(PartialEq, Debug, ArrayCast)]
#[repr(C)]
struct MyCoolColor<S> {
#[palette(unsafe_zero_sized)]
standard: PhantomData<S>,
// RgbHue is a wrapper with `#[repr(C)]`, so it can safely
// be converted straight from `f32`.
#[palette(unsafe_same_layout_as = "f32")]
hue: RgbHue<f32>,
lumen: f32,
chroma: f32,
}
let buffer = [172.0, 100.0, 0.3];
let color: MyCoolColor<Srgb> = cast::from_array(buffer);
assert_eq!(
color,
MyCoolColor {
hue: 172.0.into(),
lumen: 100.0,
chroma: 0.3,
standard: PhantomData,
}
);
§Safety
- The type must be inhabited (eg: no Infallible).
- The type must allow any values in the array items (eg: either no requirements or some ability to recover from invalid values).
- The type must be homogeneous (eg: all fields have the same type, or are
wrappers that implement
ArrayCast
with the same field type, or are zero sized). - The length of
Array
must be the sum of the number of color component fields in the type and in any possible compound fields. - The type must be
repr(C)
orrepr(transparent)
. - The type must have the same size and alignment as
Self::Array
.
Note also that the type is assumed to not implement Drop
. This will
rarely, if ever, be an issue. The requirements above ensures that the
underlying field types stay the same and will be dropped.
For example:
Srgb<T>
can be cast to[T; 3]
because it has three non-zero sized fields of typeT
.Alpha<Srgb<T>, T>
can be cast to[T; 4]
, that is3 + 1
items, because it’s the sum of the three items fromSrgb
and the one extraalpha
field.Alpha<Srgb<T>, U>
is not allowed becauseT
andU
are different types.