您描述的语法:
// should not compile because `age` is not defined
let person_b = Person {
name: "B".to_string(),
..Default::default(),
}
是不可能实现的. struct 要么实现Default
,要么不实现...
struct update syntax也只适用于相同类型的 struct .
然而,对于您所描述的semantics,有多种 Select 可以实现方便的语法:
1) Multiple new
functions:
impl Person {
fn new(name: String, age: usize) -> Self {
Self {name, age, occupation: None}
}
fn new_with_occupation(name: String, age: usize, occupation: String) -> Self {
Self {name, age, occupation: Some(occupation)}
}
}
使用示例:
let p1 = Person::new("a".to_string(), 1);
let p2 = Person::new_with_occupation("b".to_string(), 2, "o".to_string());
2)构建器模式:
#[derive(Default)]
struct PersonBuilder {
// These fields can be pub or not, depending on your tastes.
// If they are pub, you can use the .. syntax for this builder
// instead of / in addition to the field setter methods.
occupation: Option<String>,
}
impl PersonBuilder {
fn occupation(self, occupation: String) -> Self {
Self {
occupation: Some(occupation),
..self
}
}
fn build(self, name: String, age: usize) -> Person {
Person {name, age, occupation: self.occupation }
}
}
使用示例:
let p1 = PersonBuilder::default().build("a".to_string(), 1);
let p2 = PersonBuilder::default()
.occupation("b".to_string())
.build("o".to_string(), 2);
let p3 =
PersonBuilder {
occupation: Some("o".to_string()),
..Default::default()
}.build("c".to_string(), 3)
3) A sub struct for the defaultable Fields + a Deref
impl
:
#[derive(Default)]
struct PersonOptions {
occupation: Option<String>,
}
struct Person {
options: PersonOptions,
name: String,
age: usize
}
impl Deref for Person {
type Target = PersonOptions;
fn deref(&self) -> &Self::Target {
&self.options
}
}
impl DerefMut for Person {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.options
}
}
使用示例:
let p1 = Person {
name: "a".to_string(),
age: 1,
options: Default::default()
};
let p2 = Person {
name: "b".to_string(),
age: 2,
options: PersonOptions {
occupation: Some("o".to_string()),
..Default::default()
}
};
println!("{:?}", p2.occupation); // works because of `Deref`
摘要:
Approach |
Advantages |
Disadvantages |
Recommended for |
1) new functions |
Simple and readable. |
There are lots of combinations for structs with many fields. Also, the order of the parameters decides the corresponding field, which can become hard to read, and annoying to refactor if a field is added. |
Simple structs with few fields / initialization variants |
2) Builder pattern |
Scales better than 1) if you have many fields. |
More boilerplate than 1) , can lead to suboptimal code generation due to the moves of the Builder in the setter functions. |
Complex structs with many optional fields |
3) Deref |
Closest in syntax to what you wanted. |
Deref is generally discouraged for simple structs because it might be confusing to the reader. DerefMut can also lead to borrow checker issues because the whole struct is reborrowed, not just the accessed field. |
Only if you have a good reason why 1 and 2 are impractical (I can't really think of one though). |