Attributes, Arguments, and Sub-Arguments
As mentioned in the Terminology section, FieldX provides the fxstruct
macro to annotate structs and the fieldx
attribute to adjust field parameters. Both accept arguments that can take one of the following forms:
-
Flag: A pure boolean keyword where its presence means
true
and its absence meansfalse
. -
Keyword: Similar to a flag in that it represents a boolean value. However, it can also take a function-like form with a single argument, the
off
flag, which sets its value tofalse
:#[fxstruct(sync(off))]
-
List or Function: A complex argument that accepts a comma-separated list of sub-arguments. The term "list" originates from the definitions of Rust procedural macro entities:
#[fxstruct(builder(attributes_fn(allow(dead_code), opt_in)))]
-
Helper: A function-like argument bound to a method of the struct implementation. The method can either be generated by the
fxstruct
macro or defined by the user. Helpers share common arguments, such as a string literal defining the helper's name, its visibility (if applicable), a list of Rust attributes, a doc comment, and theoff
flag:#[fieldx(builder("set_foo", vis(pub(crate)), doc("Sets the foo field.")))]
The full list of provided arguments can be found in the FieldX documentation.
Struct- and Field-Level Arguments
See also: Terminology
Arguments for the struct-level fxstruct
macro serve two purposes, depending on their semantics: they either specify the struct's behavior or parameters, or define default values for field-level arguments. In the following example, the field v3
opts out of being lazily initialized and instead uses a static default value:
#[fxstruct(lazy)]
struct MyStruct {
v1: u32,
v2: f32,
#[fieldx(
lazy(off),
default("static default".to_string())
)]
v3: String,
}
Interestingly, the v3
field also provides an implicit default at the struct level! Normally, FieldX avoids generating unnecessary code, and the Default
trait implementation is no exception. However, if a field is given a default, it is as if the default
argument were specified at the struct level, as shown in this snippet:
#[fxstruct(lazy, default)]
struct MyStruct {
v1: u32,
v2: f32,
#[fieldx(
lazy(off),
default("static default".to_string())
)]
v3: String,
}
This example also demonstrates how the same arguments can have different meanings at the struct and field levels. While lazy
has a consistent semantics at both levels with the struct-level argument implicitly applied to all fields, the default
argument behaves differently. At the struct level, it provides a new trait implementation, while at the field level, it assigns a default value. The syntax also differs: the struct-level default
argument is just a keyword.
A more prominent example of the difference between struct-level and field-level is the builder
argument. At the struct level, it generates a builder type and the builder()
method. At the field level, it generates a builder method for the field. Both use the same syntax but support partially different lists of sub-arguments:
#[fxstruct(builder("MyStructBuilder", opt_in))]
struct MyStruct {
v1: u32,
#[fieldx(builder("set_v2", into))]
v2: f32,
}