#[fxstruct]Expand description
This macro implements fieldx functionality for a struct with named fields.
The macro provides two attributes: #[fxstruct] and #[fieldx]. The former is referred to as “struct level”
because it is the attribute applied to the struct itself. The latter defines individual field properties and,
correspondingly, is referred to as “field level”. Both attributes accept a number of arguments, most of which are
shared between two but may have somewhat different semantics. Also, arguments of #[fxstruct] are usually
responsible for specifying defaults for field level.
§Attribute Arguments
§Terminology
As it was mentioned above, the attributes accept arguments. I.e., for #[fxstruct(sync, rc)] words sync and rc
are the arguments. In cases when an argument semantics can be regulated with additional parameters these are called
subarguments in this documentation. For example off in #[fieldx(get(off))] is a subargument of argument get.
A subargument in some cases may also take subarguments of its own.
Arguments and subarguments are classified depending on their syntax and functionality. Here is a list of their types:
- keyword – boolean-like, only accepts
off:keyword(off) - flag – similar to the keyword above but takes no arguments; as a matter of fact, the
offabove is a flag - helper - introduce functionality that is bound to a helper method (see below)
- list or function – can take multiple sub-arguments
- meta - can take some syntax constructs
Field Type, that starts each argument description, specifies on of the above types.
§Helper Methods
These implement certain functionality either provided by fieldx or required to be provided by user. For now the
only kind of required helpers are lazy builders. Others are generated by this this macro.
§Common Sub-Arguments of Helper Arguments
Helper arguments share a bunch of common sub-arguments. They’re listed in the following table, but if their meaning is unclear then it would be better to skip this section and get back to it later.
For example:
#[fxstruct( get( "get_", public(crate) ) )]will generate accessor methods with names prefixed with get_ and visibility pub(crate):
let foo = obj.get_foo();With:
#[fieldx( get( "special_type", private ) )]
ty: String,a method of the field owning struct can use the accessor as follows:
let foo = self.special_type();§attributes* Family of Sub-Arguments
Sometimes it might be necessary to specify additional attributes for various generated syntax elements like methods,
or auxiliary structs. Where applicable, this functionality is supported by attributes* (sub)arguments. Their
syntax is attributes(<attr1>, <attr2>, ...) where an <attr> looks exactly, as it would in
the code but excluding the #[...] wrapping.
For example, attributes_fn(allow(dead_code), cfg(feature = "myfeature")) will expand into something like:
#[allow(dead_code)]
#[cfg(feature = "myfeature")]The following members of the family are currently supported: attributes, attributes_fn, and attributes_impl.
Which ones are implemented for a particular context is documented below.
§Struct Level Arguments
§attributes
Type: list
Fallback attributes for structs produced by the builder and serde arguments. I.e. when
builder or serde are requested but don’t have their own attributes
then this one will be used.
§attributes_impl
Type: list
Attributes to be applied to the struct implementation.
§sync
Type: keyword
Declare a struct as thread-safe by default.
§r#async*
Type: keyword
Declare a struct as async by default.
Note: Since async is a keyword, the syn is not allowing to use it as-is, only with the r# prefix, according
to Rust syntax.
§mode
Type: function
This is another way to specify the default concurrency mode for struct. It takes one of three keywords as arguments:
syncasyncplain
Note that contrary to the direct keyword way, async doesn’t require the r# prefix: mode(async).
Also, there is no plain keyword, but one can use it with mode as an explicit marker.
§lazy
Type: helper
Enables lazy mode for all fields except those marked with lazy(off).
§fallible
Type: function
Enable fallible lazy builders, i.e. expects them to return [Result] enum. Takes two sub-arguments:
off- disables fallible functionalityerror(ErrorType)– set the expected error, returned by builder.
fallible(off, error(MyError)) simply sets default error type for all fallible lazy fields.
§inner_mut
Type: keyword
Turns on interior mutability for struct fields by default.
§builder
Type: helper
Enables builder functionality by introducing a builder() associated function and builder type:
#[fxstruct(builder, get)]
struct Foo {
description: String,
}
let obj = Foo::builder()
.description(String::from("some description"))
.build()?;
assert_eq!(obj.description(), "some description");Literal string sub-argument of builder defines common prefix for methods-setters of the builder. For example, with
builder("set_") one would then use .set_description(...) call.
Additional sub-arguments:
-
attributes(see the section above) – builder struct attributes -
attributes_impl- attributes of the struct implementation -
into– force all builder setter methods to attempt automatic type conversion using.into()methodWith
intothe example above wouldn’t needString::fromand the call could look like this:.description("some description") -
opt_in- struct-level only argument; with it only fields with explicitbuildercan be set by builder. -
init- struct-level only argument; specifies identifier of the method to call to finish object initialization. -
post_build- struct-level only; makes builder’sbuild()method to callpost_build()method. If given an ident argument it specifies different method name:post_build(check_in).There are a couple of notes to take into account:
- the method is called on freshly created object right before it is returned back to builder caller
- it must take and return
self:fn post_build(mut self) { self.foo = "bar"; self } - for reference-counted structs the method is invoked before they’re wrapped into corresponding container;
this allows for
mut selfand direct access to the fields without use of inner mutability
-
error(ErrorType)- struct-level only; changes the error type returned by thebuild()method. Together with thepost_buildargument makes the post-build method fallible with the same error type.Note that the builder code is always producing
FieldXError::UninitializedFieldvariant. Therefore, to be compatible with it the customErrorTypemust implementFrom<FieldXError>.
§rc
Type: keyword
With this argument new instances of the type, produced by the new method or by type’s builder, will be wrapped
into reference counting pointers Rc or Arc, depending on sync status of the type.
§no_new
Type: keyword
Disable generation of method new. This is useful for cases when a user wants their own new method.
With this option the macro may avoid generating Default implementation for the struct. More details in a section
below.
§default
Type: keyword
Forces the Default implementation to be generated for the struct.
§get
Type: helper
Enables or disables getter methods for all fields, unless a field is marked otherwise.
Additionally to the standard helper arguments accessors can also be configured as:
clone- cloning, i.e. returning a clone of the field value (must implement [Clone])copy- copying, i.e. returning a copy of the field value (must implement [Copy])as_ref– only applicable if field value is optional; it makes the accessor to return anOption<&T>instead of&Option<T>
§get_mut
Type: helper
Request for a mutable accessor. Since neither of additional options of get are applicable here1
only basic helper sub-arguments are accepted.
Mutable accessors have the same name, as immutable ones, but with _mut suffix, unless given explicit name by the
user:
#[fxstruct(get, get_mut)]
struct Foo {
description: String,
}
let mut obj = Foo::new();
*obj.description_mut() = "some description".to_string();
assert_eq!(obj.description(), "some description");§set
Type: helper
Request for setter methods. If a literal string sub-argument is supplied it is used as setter method prefix instead
of the default set_.
Takes an additional sub-argument:
into: use the [Into] trait to automatically convert a value into the field type
#[fxstruct(set(into), get)]
struct Foo {
description: String,
}
let mut obj = Foo::new();
obj.set_description("some description");
assert_eq!(obj.description(), &"some description".to_string());§reader, writer
Type: helper
Only meaningful for sync structs. Request for reader and writer methods that would return either read-only or
read-write lock guards.
Akin to setters, method names are formed using read_ and write_ prefixes, correspondingly, prepended to the
field name.
#[fxstruct(sync, reader, writer)]
struct Foo {
description: String,
}
let obj = Foo::new();
{
let mut wguard = obj.write_description();
*wguard = String::from("let's use something different");
}
{
let rguard = obj.read_description();
assert_eq!(*rguard, "let's use something different".to_string());
}See the section about differences between get/get_mut and reader/writer
§lock
Type: flag
Forces lock-wrapping of all fields by default. Can be explicitly disabled with lock(off). Identical to the
reader/writer arguments but without installing any methods.
§clearer and predicate
Type: helper
These two are tightly coupled by their meaning, though can be used separately.
Predicate helper methods return bool and are the way to find out if a field is set. They’re universal in the way
that no matter wether a field is sync, or plain, or lazy, or just optional – you always use the same method.
Clearer helpers are the way to reset a field into uninitialized state. For optional fields it would simply mean it
will contain [None]. A lazy field would be re-initialized the next time it is read from.
Clearers return the current field value. If field is already uninitialized (or never has been yet) None will be
given back.
Using either of the two automatically make fields optional unless lazy.
Check out the example in the Optional Fields section.
§optional
Type: keyword
Explicitly make all fields optional. Useful when neither predicate nor clearer helpers are needed.
§public(...), private
Specify defaults for helpers. See the sub-arguments section above for more details.
§clone, copy
Specify defaults for accessor helpers.
§serde
Type: function
Enabled with serde feature, which is off by default.
Support for de/serialization will be discussed in more details in a section below. What is important to know at this
point is that due to use of container types direct serialization of a struct is hardly possible. Therefore fieldx
utilizes serde’s into and from by creating a special shadow struct. The shadow, by default, is named after the
original by prepending the name with double underscore and appending Shadow suffix: __FooShadow.
The following sub-arguments are supported:
- a string literal is used to give the shadow struct a user-specified name
offdisables de/serialization support altogetherattributes(...)- custom attributes to be applied to the shadow structpublic(...),private– specify visibility of the shadow structserialize- enable or disable (serialize(off)) serialization support for the structdeserialize- enable or disable (deserialize(off)) deserialization support for the structdefault- wetherserdemust use defaults for missing fields and, perhaps, where to take the defaults from\forward_attrs- a list of field attributes that are to be forwarded to the corresponding field of the shadow struct
§Notes about default
Valid arguments for the sub-argument are:
- a string literal that has the same meaning as for
the container-level
serdeattributedefault - a path to a symbol that is bound to an instance of our type:
my_crate::FOO_DEFAULT - a call-like path that’d be used literally:
Self::serde_default()
The last option is preferable because fieldx will parse it and replace any found Self reference with the
actual structure name making possible future renaming of it much easier.
There is a potentially useful “trick” in how default works. Internally, whatever type is returned by the
sub-argument it gets converted into the shadow type with trait [Into]. This allows you to use the original struct
as the trait implementation is automatically generated for it. See this example from a test:
#[fxstruct(sync, get, serde("BazDup", default(Self::serde_default())))]
#[derive(Clone)]
struct Baz {
#[fieldx(reader)]
f1: String,
f2: String,
}
impl Baz {
fn serde_default() -> Fubar {
Fubar {
postfix: "from fubar".into()
}
}
}
struct Fubar {
postfix: String,
}
impl From<Fubar> for BazDup {
fn from(value: Fubar) -> Self {
Self {
f1: format!("f1 {}", value.postfix),
f2: format!("f2 {}", value.postfix),
}
}
}
let json_src = r#"{"f1": "f1 json"}"#;
let foo_de = serde_json::from_str::<Baz>(&json_src).expect("Bar deserialization failure");
assert_eq!(*foo_de.f1(), "f1 json".to_string());
assert_eq!(*foo_de.f2(), "f2 from fubar".to_string());§Field Level Arguments
At this point, it’s worth refreshing your memory about sub-arguments of helpers and how they differ in
semantics between fxstruct and fieldx attributes.
§skip
Type: flag
Leave this field alone. The only respected argument of fieldx when skipped is the default.
§lazy
Type: helper
Mark field as lazy.
§fallible
Lets lazy builder method to return an error. For example:
#[fxstruct()]
struct Foo {
#[fieldx(lazy, fallible(error(MyError)))]
connection: Resource,
}
impl Foo {
fn build_connection(&self) -> Result<Resource, MyError> {
Err(MyError::AdHoc)
}
}
let foo = Foo.new();
assert!(foo.connection().is_err());Now, field accessor would be returning Result<T, MyError>, where the exact type T depends on other field
paramters.
If many struct fields need to be fallible while common error type is used for each one it would make sense to declare the type once for the entire struct:
#[fxstruct( fallible(off, error(MyError)) )]
struct Foo {
#[fieldx(lazy, fallible)]
connection: Resource,
#[fieldx(lazy, fallible)]
resource_manager: ResourceManager,
}§inner_mut*
Type: keyword
Enables field interior mutability.
§rename
Type: function
Specify alternative name for the field. The alternative will be used to form method names and, with serde feature
enabled, serialization name2.
§get, get_mut, set, reader, writer, clearer, predicate, optional
Type: helper
Have similar syntax and semantics to corresponding fxstruct arguments:
§optional
Type: keyword
Explicitly mark field as optional even if neither predicate nor clearer are requested.
§public(...), private
Field-default visibility for helper methods. See the sub-arguments section above for more details.
§serde
Type: function
At the field-level this option acts mostly the same way, as at the struct-level. With a couple of differences:
- string literal sub-argument is bypassed into
serdefield-levelrename defaultis responsible for field default value; contrary to the struct-level, it doesn’t use [Into] traitattributeswill be applied to the field itselfserialize/deserializecontrol field marshalling
§into
Type: keyword
Sets default for set and builder arguments.
§builder
Type: helper
Mostly identical to the struct-level builder. Field specifics are:
- no
attributes_implandopt_in(consumed, but ignored) - string literal specifies setter method name of the builder type for this field
attributesandattributes_fnare correspondingly applies to builder field and builder setter method
Field level only argument:
required– this field must always get a value from the builder even if otherwise it’d be optional