Attribute Macro varlen::define_varlen
source · [−]#[define_varlen]
Expand description
Macro for defining variable-length structs.
Examples
A struct with multiple variable-length fields, specified by #[varlen]
:
use varlen::prelude::*;
#[define_varlen]
struct Person {
age: usize,
#[varlen]
name: Str,
#[varlen]
email: Str,
}
let mut p: VBox<Person> = VBox::new(
person::Init{
age: 16,
name: Str::copy("Harry Potter"),
email: Str::copy("harry.potter@example.com"),
}
);
assert_eq!(p.age, 16);
assert_eq!(&p.refs().name[..], "Harry Potter");
p.as_mut().muts().name.mut_slice().make_ascii_uppercase();
assert_eq!(&p.refs().name[..], "HARRY POTTER");
assert_eq!(&p.refs().email[..], "harry.potter@example.com");
A struct with variable-length arrays. The arrays are annotated with
#[varlen_array]
. You may directly specify the array lengths as
any const
-evaluatable expression that references the fields
annotated with #[controls_layout]
:
use varlen::prelude::*;
#[define_varlen]
struct MultipleArrays {
#[controls_layout]
len: usize,
#[varlen_array]
array1: [u16; *len],
#[varlen_array]
array2: [u8; *len],
#[varlen_array]
half_array: [u16; (*len) / 2],
}
let base_array = vec![1, 3, 9, 27];
let a: VBox<MultipleArrays> = VBox::new(multiple_arrays::Init{
len: base_array.len(),
array1: FillSequentially(|i| base_array[i]),
array2: FillSequentially(|i| base_array[base_array.len() - 1 - i] as u8),
half_array: FillSequentially(|i| base_array[i * 2]),
});
assert_eq!(a.refs().array1, &[1, 3, 9, 27]);
assert_eq!(a.refs().array2, &[27, 9, 3, 1]);
assert_eq!(a.refs().half_array, &[1, 9]);
Attributes
The following attributes are available on fields:
#[varlen]
- use this for a field type which implementsVarLen
#[varlen_array]
- use this for an array-typed field. The length of the array may refer to fields annotated with#[controls_layout]
, and must be aconst
-evaluatable expression#[controls_layout]
- this marks a field as usable to specify the length of a#[varlen_array]
field. It also removes mutable access to the field. Mutable access risks causing memory unsafety, in which the length of the array is recorded incorrectly in memory.
Generated API
See crate varlen_generated
for an example of the generated code.
For a struct MyType
, the following is generated:
- A type
MyType
, with variable-length fields replaced with marker typesvarlen::marker::FieldMarker
andvarlen::marker::ArrayMarker
- A module
my_type
(the snake-case version of the struct name), containing:- a type
VarLenLayout
which specifies the layout ofMyType
- a type
Refs<'a>
consisting of immutable references, with lifetime'a
- a type
Muts<'a>
consisting of mutable references, with lifetime'a
- a type
Init<...>
which implementsvarlen::Initializer<MyType>
- a type
LayoutControllers
, which consists of the fields annotated#[controls_layout]
- a type
- Various member functions on
MyType
, for immutable and mutable access. - Trait implementations for
MyType
:VarLen
forMyType
Drop
forMyType
, which unconditionally panics.
Memory layout
The object is laid out in memory with the fixed-length fields first. These are
the fields which are not annotated with #[varlen]
or #[varlen_array]
. The
fixed-length fields are laid out in whatever order is chosen by the Rust compiler.
The variable-length fields always follow the fixed-length fields in memory. The variable-length fields are always laid out in the order specified in the struct definition. Padding is inserted between variable-length fields as needed to meet the alignment requirements of each field.
Padding before variable-length fields costs both memory and time. To minimize padding effects, as much as possible try to use the same alignment for all fields. Where this is not possible, try to order your fields from most-aligned to least-aligned: when a less-aligned field follows a more-aligned field, there are zero padding bytes, and the padding computation can be entirely optimized away.
Access to a variable-length field requires running some code at runtime that skips over all previous variable-length fields in the struct. To minimize time spent on skipping over these fields, you can:
- Store the result of
refs()
in a local variable, and reuse that variable multiple times. This calculates the beginning of all variable-length fields once, and then reuses that calculation. - Sort the variable-length fields of the struct so that the most-frequently-used fields come first.