我有一个名为Foo
的结构体,它代表了一种外部序列化格式。 Foo
有数十个字段,而且还在不断添加新的字段。值得庆幸的是,所有新添加的字段都保证有合理的默认值。
Rust语言提供了一种很好的语法,可以使用默认值创建结构体,然后更新选定的几个值:
Foo {
bar: true,
..Default::default()
}
同样地,我们可以使用类型为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)
如果有数十个字段,那么这将变得很繁琐。
我该如何解决这个问题?
phantom
重命名为__phantom
,将其公开并添加#[doc(hidden)]
属性。具体示例:std::io::ErrorKind::__Nonexhaustive
。 - mcartonstd
有点特殊,因为它可以声明__Nonexhaustive
变体不稳定,这样如果调用者使用的是稳定版 Rust,则无法使用它。采用相同技巧(这是我绝对会做的事情)的库编写者必须依赖于约定。 (实际上我认为这不是一个真正的问题,我只是在挑剔。) - BurntSushi5PhantomData
。感谢您提供的这个有用的示例! - ljedrz#[deprecated]
在这里可以工作。 - mcarton