How to make some Struct's fields mandatory to fill and others optional in Rust?
I have some basic struct to modeling an item's unit, eg: pcs, box, dozen, etc.
But I need to make some fields mandatory to be defined by user and some are not. Here's my implementation with default
constructor from Rust's documentation. My problem is that Rust forced all of the field to be defined in the constructor:
pub struct Unit {
pub name: String, // this is mandatory. MUST be filled
pub multiplier: f64, // this is mandatory. MUST be filled
pub price_1: Option<f64>, // this is non-mandatory with default value NONE
pub price_2: Option<f64>, // this is non-mandatory with default value NONE
pub price_3: Option<f64>, // this is non-mandatory with default value NONE
}
// here I implement the Default just for the prices.
// If user doesn't fill the name and multiplier field, it will throws an error
// the problem is that Rust forced all of the field to be defined in the constructor
impl Default for Unit {
fn default() -> Unit {
Unit {
price_1: None,
price_2: None,
price_3: None,
}
}
}
let u = Unit {
name: String::from("DOZEN"), // user must fill the name field
multiplier: 20.0, // also the multiplier field
price_1: Some(25600.0), // this is optional, user doesn't have to define this
..Default::default() // call the default function here to populate the rest
}
Solution 1:
You can detach the default implementation into an external struct, and then make a constructor that just needs the mandatory items:
pub struct Unit {
pub name: String, // this is mandatory. MUST be filled
pub multiplier: f64, // this is mandatory. MUST be filled
pub prices: Prices
}
pub struct Prices {
pub price_1: Option<f64>, // this is non-mandatory with default value NONE
pub price_2: Option<f64>, // this is non-mandatory with default value NONE
pub price_3: Option<f64>, // this is non-mandatory with default value NONE
}
impl Default for Prices {
fn default() -> Prices {
Prices {
price_1: Default::default(),
price_2: Default::default(),
price_3: Default::default()
}
}
}
impl Unit {
pub fn new(name: String, multiplier: f64) -> Self {
Unit {name, multiplier, prices: Default::default()}
}
}
Playground
Solution 2:
You could create an associated function, from_name_and_multiplier()
, for creating your Unit
values with a name and multiplier:
impl Unit {
pub fn from_name_and_multiplier(name: String, multiplier: f64) -> Self {
Self {
name,
multiplier,
price_1: None,
price_2: None,
price_3: None,
}
}
}
This function forces you to provide both name
and multiplier
. Then, you can use this returned Unit
value to initialize the name
and multiplier
fields of another Unit
:
let u = Unit {
price_1: Some(25600.0),
..Unit::from_name_and_multiplier("DOZEN".to_string(), 20.0)
};
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1a871a9634d39bf83168c5d51b39b236