我有一个Foo表示外部序列化格式的结构.Foo有几十个领域,而且还有更多的领域.令人高兴的是,所有新领域都保证有合理的默认值.
Rust有一个很好的语法,可以使用默认值创建结构,然后更新一些选定的值:
Foo {
bar: true,
..Default::default()
}
Run Code Online (Sandbox Code Playgroud)
类似地,我们可以使用类型的私有字段来表示"此结构在未来版本中可能包含更多字段"的概念PhantomData.
但如果我们将这两个习语结合起来,就会出现错误:
use std::default::Default;
mod F {
use std::default::Default;
use std::marker::PhantomData;
pub struct Foo {
pub bar: bool,
phantom: PhantomData<()>,
}
impl Default for Foo {
fn default() -> Foo {
Foo {
bar: false,
phantom: PhantomData,
}
}
}
}
fn main() {
F::Foo {
bar: true,
..Default::default()
};
}
Run Code Online (Sandbox Code Playgroud)
这给了我们错误:
error: field `phantom` of struct `F::Foo` is private [--explain E0451]
--> <anon>:23:5
|>
23 |> F::Foo {
|> ^
Run Code Online (Sandbox Code Playgroud)
从逻辑上讲,我认为这应该有效,因为我们只是更新公共字段,这将是有用的习惯用法.另一种方法是支持以下内容:
Foo::new()
.set_bar(true)
Run Code Online (Sandbox Code Playgroud)
......几十个领域都会变得单调乏味.
我该如何解决这个问题?
默认字段语法不起作用,因为您仍在创建新实例(即使您尝试从另一个对象获取某些字段值).
另一种方法是支持以下内容:
Run Code Online (Sandbox Code Playgroud)Foo::new() .set_bar(true)......几十个领域都会变得单调乏味.
我不确定即使有很多领域,这个:
Foo::new()
.set_bar(true)
.set_foo(17)
.set_splat("Boing")
Run Code Online (Sandbox Code Playgroud)
比以下更加繁琐:
Foo {
bar: true,
foo: 17,
splat: "Boing",
..Foo::default()
}
Run Code Online (Sandbox Code Playgroud)
或者,您可以将公共字段分离为自己的类型:
pub struct FooPub {
pub bar: bool,
// other pub fields
}
pub struct Foo {
pub bar: bool,
// other pub fields
// alternatively, group them: pub public: FooPub,
foo: u64,
}
impl Foo {
pub fn new(init: FooPub) {
Foo {
bar: init.bar,
// other pub fields
// alternative: public: init
// private fields
foo: 17u64,
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你将它称为:
Foo::new(FooPub{ bar: true })
Run Code Online (Sandbox Code Playgroud)
或添加一个fn FooPub::default()以允许您默认某些字段:
Foo::new(FooPub{ bar: true, ..FooPub::default()})
Run Code Online (Sandbox Code Playgroud)
重命名phantom到__phantom,把它公开和#[doc(hidden)].
use std::default::Default;
mod foo {
use std::default::Default;
use std::marker::PhantomData;
pub struct Foo {
pub bar: bool,
// We make this public but hide it from the docs, making
// it private by convention. If you use this, your
// program may break even when semver otherwise says it
// shouldn't.
#[doc(hidden)]
pub _phantom: PhantomData<()>,
}
impl Default for Foo {
fn default() -> Foo {
Foo {
bar: false,
_phantom: PhantomData,
}
}
}
}
fn main() {
foo::Foo {
bar: true,
..Default::default()
};
}
Run Code Online (Sandbox Code Playgroud)
这是一个不那么罕见的模式,现场例子:std::io::ErrorKind::__Nonexhaustive.
当然,如果他们选择使用某个__named领域,用户将不会有任何警告或任何东西,但是这__意味着非常明确.如果需要警告,#[deprecated]可以使用.