Expand description
This crate provides a custom derive (#[derive(StructOfArray)]
) to
automatically generate code from a given struct T
that allow to replace
Vec<T>
with a struct of arrays. For example, the following code
#[derive(StructOfArray)]
pub struct Cheese {
pub smell: f64,
pub color: (f64, f64, f64),
pub with_mushrooms: bool,
pub name: String,
}
will generate a CheeseVec
struct that looks like this:
pub struct CheeseVec {
pub smell: Vec<f64>,
pub color: Vec<(f64, f64, f64)>,
pub with_mushrooms: Vec<bool>,
pub name: Vec<String>,
}
It will also generate the same functions that a Vec<Chees>
would have, and
a few helper structs: CheeseSlice
, CheeseSliceMut
, CheeseRef
and
CheeseRefMut
corresponding respectivly to &[Cheese]
, &mut [Cheese]
,
&Cheese
and &mut Cheese
.
§How to use it
Add #[derive(StructOfArray)]
to each struct you want to derive a struct of
array version. If you need the helper structs to derive additional traits
(such as Debug
or PartialEq
), you can add an attribute #[soa_derive = "Debug, PartialEq"]
to the struct declaration.
#[derive(Debug, PartialEq, StructOfArray)]
#[soa_derive(Debug, PartialEq)]
pub struct Cheese {
pub smell: f64,
pub color: (f64, f64, f64),
pub with_mushrooms: bool,
pub name: String,
}
If you want to add attribute to a specific generated struct(such as
#[cfg_attr(test, derive(PartialEq))]
on CheeseVec
), you can add an
attribute #[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))]
to the
struct declaration.
#[derive(Debug, PartialEq, StructOfArray)]
#[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))]
pub struct Cheese {
pub smell: f64,
pub color: (f64, f64, f64),
pub with_mushrooms: bool,
pub name: String,
}
Mappings for first argument of soa_attr
to the generated struct for Cheese
:
Vec
=>CheeseVec
Slice
=>CheeseSlice
SliceMut
=>CheeseSliceMut
Ref
=>CheeseRef
RefMut
=>CheeseRefMut
Ptr
=>CheesePtr
PtrMut
=>CheesePtrMut
§Usage and API
All the generated code have some generated documentation with it, so you
should be able to use cargo doc
on your crate and see the documentation
for all the generated structs and functions.
Most of the time, you should be able to replace Vec<Cheese>
by
CheeseVec
, with exception of code using direct indexing in the vector and
a few other caveats listed below.
§Caveats and limitations
Vec<T>
functionalities rely a lot on references and automatic deref
feature, for getting function from [T]
and indexing. But the SoA vector
(let’s call it CheeseVec
, generated from the Cheese
struct) generated by
this crate can not implement Deref<Target=CheeseSlice>
, because Deref
is
required to return a reference, and CheeseSlice
is not a reference. The
same applies to Index
and IndexMut
trait, that can not return
CheeseRef/CheeseRefMut
.
This means that the we can not index into a CheeseVec
, and that a few
functions are duplicated, or require a call to as_ref()/as_mut()
to change
the type used.
§Iteration
It is possible to iterate over the values in a CheeseVec
let mut vec = CheeseVec::new();
vec.push(Cheese::new("stilton"));
vec.push(Cheese::new("brie"));
for cheese in vec.iter() {
// when iterating over a CheeseVec, we load all members from memory
// in a CheeseRef
let typeof_cheese: CheeseRef = cheese;
println!("this is {}, with a smell power of {}", cheese.name, cheese.smell);
}
One of the main advantage of the SoA layout is to be able to only load some fields from memory when iterating over the vector. In order to do so, one can manually pick the needed fields:
for name in &vec.name {
// We get referenes to the names
let typeof_name: &String = name;
println!("got cheese {}", name);
}
In order to iterate over multiple fields at the same time, one can use the soa_zip! macro.
for (name, smell, color) in soa_zip!(&mut vec, [name, mut smell, color]) {
println!("this is {}, with color {:#?}", name, color);
// smell is a mutable reference
*smell += 1.0;
}
§Nested Struct of Arrays
In order to nest a struct of arrays inside another struct of arrays, one can use the #[nested_soa]
attribute.
For example, the following code
#[derive(StructOfArray)]
pub struct Point {
x: f32,
y: f32,
}
#[derive(StructOfArray)]
pub struct Particle {
#[nested_soa]
point: Point,
mass: f32,
}
will generate structs that looks like this:
pub struct PointVec {
x: Vec<f32>,
y: Vec<f32>,
}
pub struct ParticleVec {
point: PointVec, // rather than Vec<Point>
mass: Vec<f32>
}
All helper structs will be also nested, for example PointSlice
will be nested in ParticleSlice
.
§Use in a generic context
StructOfArray
does not provide a set of common operations by default. Thus if you wanted to use a StructOfArray
type in a generic context, there is no way to guarantee to the type system that any methods are available.
This will also generate implementations of SoAVec
, SoASlice
, and SoASliceMut
for the respective
Vec
, Slice
and SliceMut
types. These rely on GATs, and so require Rust 1.65 or newer.
#[derive(StructOfArray)]
pub struct Point {
x: f32,
y: f32,
}
fn get_num_items<T: StructOfArray, V: SoAVec<T>>(values: &V) -> usize {
values.len()
}
Macros§
- soa_zip
- Create an iterator over multiple fields in a Struct of array style vector.
Traits§
- Into
SoAIter - A trait to express the
IntoIterator
guarantee ofSoASlice
types in the type system. - SoAAppend
Vec - A trait to implement
Clone
-dependent behavior to extend anSoAVec
with data copied from its associatedSlice
type. - SoAIndex
- Helper trait used for indexing operations.
Inspired by
std::slice::SliceIndex
. - SoAIndex
Mut - Helper trait used for indexing operations returning mutable references.
Inspired by
std::slice::SliceIndex
. - SoAIter
- Any struct derived by StructOfArray will auto impl this trait.
- SoAPointers
- This trait is automatically implemented by the relevant generated by
StructOfArray
. - SoASlice
- The interface for the
Slice
immutable slice struct-of-arrays type. - SoASlice
Mut - The interface for the
SliceMut
mutable slice struct-of-arrays type. A generalization ofSoASlice
whose methods can modify elements of the arrays - SoAVec
- The interface for the
Vec
-like struct-of-arrays type. A generalization ofSoASliceMut
whose methods can also re-size the underlying arrays. - Struct
OfArray - Any struct derived by StructOfArray will auto impl this trait You can use
<Cheese as StructOfArray>::Type
instead of explicit named typeCheeseVec
; This will helpful in generics programing that generate struct can be expressed as<T as StructOfArray>::Type
- ToSoA
Vec - A trait to implement
Clone
-dependent behavior to convert a non-owning SoA type into an owningSoAVec
.