我正在尝试在 Rust 中创建一个结构,它需要定义一些字段,但不需要定义其他字段。
例如,在下面的示例结构中,只有职业
是一个选项。因此,我想同时要求 name
和 age
,但如果不是的话,职业
可以默认为 None
已定义。
struct Person {
name: String,
age: usize,
occupation: Option<String>,
}
// should compile
let person_a = Person {
name: "A".to_string(),
age: 42,
..Default::default(),
}
// should not compile because `age` is not defined
let person_b = Person {
name: "B".to_string(),
..Default::default(),
}
// should compile
let person_c = Person {
name: "C".to_string(),
age: 42,
occupation: "Software engineer".to_string(),
}
这在 Rust 中可能吗?
我不需要坚持使用这个确切的 API。其他 API(包括宏)也是可以接受的。
最佳答案
您描述的语法:
// should not compile because `age` is not defined
let person_b = Person {
name: "B".to_string(),
..Default::default(),
}
不可能实现。结构体要么实现 Default
,或者没有。 ..
struct update syntax也仅适用于相同类型的结构。
有多种选项可以为您描述的语义实现方便的语法:
1) 多个 new
功能:
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 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`
摘要:
new
功能1)
如果您有很多字段。1)
更多样板,由于 Builder 在 setter 函数中的移动,可能会导致代码生成不理想。Deref
Deref
一般是discouraged对于简单的结构,因为它可能会让读者感到困惑。 DerefMut
还可能导致借用检查器问题,因为整个结构都被重新借用,而不仅仅是访问的字段。关于rust - 定义一个结构体,其中只能默认某些字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77538713/