The std Default derive with more customization available and some upgrades.
This crate provide a single derive trait called Default
. This derive act as the std Default
derive, but allows to modify the default values of each fields. It also allows to mark enum variants with fields as default.
- Does everything the std
Default
derive trait does - Support marking enum variant with fields as default
- Support overriding the default value of each fields
- Support no-std, which means it will output code which is no-std. Note that this library by itself needs the std library.
See all those features in actions in the Examples
chapter.
Before doing anything here, if you want to override the fields of an enum variant, you should mark it as default first
use better_default::Default;
#[derive(Default)]
enum Enum {
#[default]
Variant {
...
},
...
}
There a two ways of overriding the default values : using the per-field attributes or the top default attributes.
The per-field attributes are simply attributes you put atop of the fields for which you want to override the default values.
The syntax is the following :
#[default( <expression> )]
<field_ident>: <field_type>
You can put anything you want in the expression
bloc, as long as it can be correctly parse by syn::Expr.
Here is an example of this approach in action :
use better_default::Default;
#[derive(Default, Debug)]
enum Enum {
#[default] // mark the variant as default (this is mandatory)
Variant1 {
#[default(1)] // set the default value of `first` to 1
first: u32,
// keep the default value for `second`
second: String,
},
Variant2,
Variant3,
}
fn main() {
let default = Enum::default();
// should print "Variant1 { first: 1, second: "" }"
println!("{:?}", default);
}
Instead of placing an attribute on all the fields of a struct / enum variant, you place only on attribute atop of it, containing all the default values overrides.
The syntax of the top attributes is the following :
use better_default::Default;
#[derive(Default)]
#[default((<field_id>: <expression>),*)]
struct Struct {
...
} // the struct can have unnamed fields
#[derive(Default)]
enum Enum {
#[default((<field_id>: <expression>),*)]
Variant { ... } // the variant can have unnamed fields
}
field_id
here can means two things : if you deal with named fields, you should put the field ident here. If you deal with unnamed fields, then you should put the position of the field (0 for the first, 1 for the second, etc.).
Again, you can put anything you want in the expression
bloc, as long as it can be correctly parse by syn::Expr.
Here are two examples, one covering unnamed fields and one covering named ones.
use better_default::Default;
#[derive(Default, Debug)]
enum Enum {
// mark the variant as default, and also specifies the default values :
// - the default value of the first field (which is at index 0) is set to 2
// - the second field (which is at index 1) will have it's default value set to "Hello world!"
#[default(0: 2, 1: "Hello world!".to_string())]
Variant1(u32, String),
Variant2,
Variant3,
}
fn main() {
let default = Enum::default();
// should print "Variant1(2, "Hello world!")"
println!("{:?}", default);
}
use better_default::Default;
#[derive(Default, Debug)]
#[default(field1: 1, field2: "Hello world!".to_string())]
struct Struct {
field1: u32,
field2: String
}
fn main() {
let default = Struct::default();
println!("{:?}", default) // should print "Struct { field1: 1, field2: "Hello world!" }"
}
One last note : these two approaches can be combined, which means you can have a top attribute containing some default values while some of the fields have their own attribute.
- The per-field way : Usage of per-field attributes
Per field attributes are more suitable for struct / enum variants with named fields.
use better_default::Default;
#[derive(Default, Debug)]
enum Enum {
#[default] // mark the variant as default (this is mandatory)
Variant1 {
#[default(1)] // set the default value of `first` to 1
first: u32,
// keep the default value for `second`
second: String,
},
Variant2,
Variant3,
}
fn main() {
let default = Enum::default();
// should print "Variant1 { first: 1, second: "" }"
println!("{:?}", default);
}
use better_default::Default;
#[derive(Default, Debug)]
// Structs don't need to be mark as default with a top attribute. They're optional.
struct Struct {
#[default(10)] // set the default value of field1 to be 10
field1: u32,
// keeps the usual default value for field2
field2: String,
}
fn main() {
let default = Struct::default();
println!("{:?}", default) // should print "Struct { field1: 10, field2: "" }"
}
While not recommended, you can also use them on unnamed fields :
use better_default::Default;
#[derive(Default, Debug)]
// Structs don't need to be mark as default with a top attribute. They're optional.
struct Struct (
#[default(10)] // set the default value of field1 to be 10
u32,
// keeps the usual default value for field2
String,
);
fn main() {
let default = Struct::default();
println!("{:?}", default) // should print "Struct(10, "")"
}
use better_default::Default;
#[derive(Default, Debug)]
enum Enum {
#[default] // mark the variant as default (this is mandatory)
Variant1 (
#[default(1)] // set the default value to 1
u32,
// keep the default value
String,
),
Variant2,
Variant3,
}
fn main() {
let default = Enum::default();
// should print "Variant1(1, "")"
println!("{:?}", default);
}
- The all at once way : Usage of top default attributes
The particularity of the top attribute is that you can define all the default values at the same place.
Not all the fields need to be represented here, only those you want to modify.
use better_default::Default;
#[derive(Default, Debug)]
enum Enum {
// mark the variant as default, and also specifies the default values :
// - the first field keeps it's usual default value.
// - the second field (which is at index 1) will have it's default value set to "Hello world!"
#[default(1: "Hello world!".to_string())]
Variant1(u32, String),
Variant2,
Variant3,
}
fn main() {
let default = Enum::default();
// should print "Variant1(0, "Hello world!")"
println!("{:?}", default);
}
use better_default::Default;
#[derive(Default, Debug)]
// here we can use the top default attribute to customize the default values of our fields.
// - we change the default value of the first field (represented by the index 0) to 1
#[default(0: 1, 1: "a".to_string())]
struct Struct(u32, String);
fn main() {
let default = Struct::default();
println!("{:?}", default) // should print "Struct(1, "a")"
}
This can also be used on named fields :
use better_default::Default;
#[derive(Default, Debug)]
enum Enum {
// mark the variant as default, and also specifies the default values :
// - the first field keeps it's usual default value.
// - the second field (field2) will have it's default value set to "Hello world!"
#[default(field2: "Hello world!".to_string())]
Variant1 {
field1: u32,
field2: String
},
Variant2,
Variant3,
}
fn main() {
let default = Enum::default();
// should print "Variant1 { 0, "Hello world!" }"
println!("{:?}", default);
}
use better_default::Default;
#[derive(Default, Debug)]
#[default(field1: 1, field2: "Hello world!".to_string())]
struct Struct {
field1: u32,
field2: String
}
fn main() {
let default = Struct::default();
println!("{:?}", default) // should print "Struct { field1: 1, field2: "Hello world!" }"
}
You can contribute to the project by making a pull request.
Here are the tools i use for this library :
- rustdoc-include, which allows me to import the readme directly into the
lib.rs
without copying. That's why you can see those// #[include_doc(...)]
inlib.rs
. Use thebuild_crate_doc
script in thescripts
folder to update them.
Licensed under Apache 2.0.