#[derive(Arraygen)]
{
// Attributes available to this derive:
#[gen_array]
#[in_array]
}
Expand description
The Arraygen
derive allows you to use the attribute gen_array
at the struct level, and the attribute in_array
in each contained field.
gen_array
With gen_array
you can declare your Arraygen
methods in the following way:
#[gen_array(?visibility fn your_method_name: YourReturnType)]
- ?visibility: This placeholder is optional. You can let it blank entirely. Or you can write
pub
,pub(crate)
, or any other pub variant. - your_method_name: This is meant to be any valid method name, following the standard rules. You can’t use a name taken by another method in the struct impl. This restriction also includes other
Arraygen
methods. - YourReturnType: The return type can be any Rust type that can appear in a struct field. Notice that if the
type
does not implement the traitCopy
, you are better returning&type
or&mut type
instead, to avoid ownership errors.
There is no limit to the number of methods you can declare.
By default, these new Arraygen
methods return arrays of length 0. That’s not very useful, but that’s why we also have the next attribute: in_array
.
in_array
With in_array
you select which field is returned by which method generated by gen_array
.
#[in_array(your_method_name)]
your_method_name
: This needs to match the name of some method declared in the same struct by thegen_array
attribute.
This is the way to fill up your Arraygen
methods. The only thing you need to care about is that the type returned by your_method_name
needs to be compatible with the type of the field with the in_array
attribute.
Notice that in Rust, non-reference field types can be returned as references, but not the other way around. Or in other words. This is good:
#[derive(Arraygen)]
#[gen_array(fn references: &i32)]
struct Test {
#[in_array(references)]
data: i32
}
But this is bad:
#[derive(Arraygen)]
#[gen_array(fn non_references: i32)]
struct Test<'a> {
#[in_array(non_references)]
data: &'a i32
}
Is also good to know that the same field can be included in many Arraygen
methods, not just in only one.
You will see what I mean by checking the following example:
#[derive(Arraygen)]
#[gen_array(fn odds: i32)]
#[gen_array(fn evens: i32)]
#[gen_array(fn primes: i32)]
struct Numbers {
#[in_array(odds)]
one: i32,
#[in_array(evens)]
#[in_array(primes)]
two: i32,
#[in_array(odds, primes)] // This syntax is also valid, by the way.
three: i32,
#[in_array(evens)]
four: i32,
#[in_array(odds, primes)]
five: i32
}
let numbers = Numbers {
one: 1,
two: 2,
three: 3,
four: 4,
five: 5
};
assert_eq!(numbers.odds(), [1, 3, 5]);
assert_eq!(numbers.evens(), [2, 4]);
assert_eq!(numbers.primes(), [2, 3, 5]);
Additionally, you may also add decorators to your in_array
attribute.
#[in_array(your_method_name { comma_separated_decorators })]
Possible decorators are:
- cast : This decorator casts the current field to the return type of the
gen_array
method where it will be included. - unsafe_transmute : This one uses
unsafe { std::mem::transmute }
to force an unsafe cast of the current field to the return type of thegen_array
method. - override_implicit : In case the current field is already selected by an
implicit_select_all
clause for thisgen_array
(more about this clause later), you may useoverride_implicit
to apply different decorators to the current field.
Casting example:
#[derive(Arraygen)]
#[gen_array(fn all: i32)]
struct Numbers {
#[in_array(all { cast })]
one: f32,
#[in_array(all { cast })]
two: u8,
#[in_array(all { cast })]
three: bool,
}
let numbers = Numbers {
one: 1.0,
two: 1,
three: true
};
assert_eq!(numbers.all(), [1, 1, 1]);
Trait Objects
A very good use-case for Arraygen
consists of extracting Trait Objects from different concrete types, so you can operate in all of them at once.
trait Animal {
fn talk(&self) -> &'static str;
}
#[derive(Arraygen)]
#[gen_array(fn get_animals: &dyn Animal)]
struct Animals {
#[in_array(get_animals)]
dogo: Dog,
#[in_array(get_animals)]
tiger: Cat,
#[in_array(get_animals)]
bacon: Pig,
}
let animals = Animals {
dogo: Dog {},
tiger: Cat {},
bacon: Pig {}
};
let talk: Vec<&'static str> = animals
.get_animals()
.iter()
.map(|animal| animal.talk())
.collect();
assert_eq!(talk, ["bark", "meow", "oink"]);
And a more realistic example could be this other one:
trait SetNone {
fn set_none(&mut self);
}
impl<T> SetNone for Option<T> {
fn set_none(&mut self) {
*self = None;
}
}
#[derive(Arraygen)]
#[gen_array(fn ephemeral_options: &mut dyn SetNone)]
struct ManyOptions {
#[in_array(ephemeral_options)]
a: Option<i32>,
#[in_array(ephemeral_options)]
b: Option<String>,
c: Option<String>,
}
let mut many = ManyOptions {
a: Some(42),
b: Some(String::from("foo")),
c: Some(String::from("bar"))
};
for option in many.ephemeral_options() {
option.set_none();
}
assert_eq!(many.a, None);
assert_eq!(many.b, None);
assert_eq!(many.c, Some(String::from("bar")));
With ad-hoc traits and Arraygen
is very easy to generalize common transformations with simple one-liners.
Implicit selection of Fields by their Types
You may omit entirely the in_array
attribute if you add the implicit_select_all
clause at the end of your gen_array
declarations.
#[gen_array(?visibility fn your_method_name: YourReturnType, implicit_select_all: Type1, Type2, Type3)]
You may place either a single type in your implicit_select_all
clause or a list of comma-separated types.
As an example, by adding “implicit_select_all: f32
” to our gen_array
method, we’ll add all the fields that are of type f32
in the current struct
, as shown in the following code:
#[derive(Arraygen)]
#[gen_array(fn get_all_prices: f32, implicit_select_all: f32)]
struct ImplicitPrices {
pub water: f32,
pub oil: f32,
pub tomato: f32,
pub chocolate: f32,
}
let prices = ImplicitPrices {
water: 2.0,
oil: 4.0,
tomato: 3.0,
chocolate: 5.0,
};
assert_eq!(prices.get_all_prices().iter().sum::<f32>(), 14.0);
The implicit_select_all
clause may also include decorators:
#[gen_array(?visibility fn your_method_name: YourReturnType, implicit_select_all { comma_separated_decorators }: MatchingFieldTypes)]
See which decorators you may use in the previous in_array
section.
Implicit selection of Fields with Type Wildcards
You may use Type Wildcards (_
) on the implicit_select_all
clause.
For example, the next expression will match all fields regardless of their type (this might be used in conjunction with decorators for casting between types):
#[gen_array(fn all_fields: f32, implicit_select_all: _)]
Type Wildcards may also be used within a more complex Type definition, like Option < _ >
or Result < f32, _ >
.
Example:
#[derive(Arraygen, Debug)]
#[gen_array(fn options: &mut dyn ResetOption, implicit_select_all: Option<_>)]
struct Options {
pub a: Option<i32>,
pub b: Option<bool>
}
impl<T> ResetOption for Option<T> {
fn reset(&mut self) {
*self = None;
}
}
trait ResetOption {
fn reset(&mut self);
}
let mut options = Options {
a: Some(1),
b: Some(true)
};
options.options().into_iter().for_each(|o| o.reset());
assert_eq!(format!("{:?}", options), "Options { a: None, b: None }");
As you may see above, using Type Wildcards in conjuction with Trait Objects allows you to accomplish very powerful constructs in a very succinct manner.