[英]Updating public fields of Rust structs which have private fields
我有一個結構Foo
,代表一個外部序列化格式。 Foo
有幾十個領域,而且還有更多領域。 令人高興的是,所有新領域都保證有合理的默認值。
Rust有一個很好的語法,可以使用默認值創建結構,然后更新一些選定的值:
Foo {
bar: true,
..Default::default()
}
類似地,我們可以使用PhantomData
類型的私有字段來表示“此結構在未來版本中可能包含更多字段”的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()
};
}
這給了我們錯誤:
error: field `phantom` of struct `F::Foo` is private [--explain E0451]
--> <anon>:23:5
|>
23 |> F::Foo {
|> ^
從邏輯上講,我認為這應該有效,因為我們只是更新公共字段,這將是有用的習慣用法。 另一種方法是支持以下內容:
Foo::new()
.set_bar(true)
......幾十個領域都會變得單調乏味。
我該如何解決這個問題?
默認字段語法不起作用,因為您仍在創建新實例(即使您嘗試從另一個對象獲取某些字段值)。
另一種方法是支持以下內容:
Foo::new() .set_bar(true)
......幾十個領域都會變得單調乏味。
我不確定即使有很多領域,這個:
Foo::new()
.set_bar(true)
.set_foo(17)
.set_splat("Boing")
比以下更加繁瑣:
Foo {
bar: true,
foo: 17,
splat: "Boing",
..Foo::default()
}
或者,您可以將公共字段分離為自己的類型:
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,
}
}
}
然后你將它稱為:
Foo::new(FooPub{ bar: true })
或者添加一個fn FooPub::default()
來默認一些字段:
Foo::new(FooPub{ bar: true, ..FooPub::default()})
將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()
};
}
這是一個不太常見的模式,實例: std::io::ErrorKind::__Nonexhaustive
。
當然,如果用戶選擇使用__named
字段,用戶將不會有任何警告或任何內容,但是__
使得意圖非常明確。 如果需要警告,可以使用#[deprecated]
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.