我有一个struct Foo,它表示一种外部序列化格式.Foo有几十个字段,并且一直在添加更多字段.幸运的是,所有新字段都保证有合理的默认值.

Rust有一个很好的语法,可以使用默认值创建 struct ,然后更新一些选定的值:

Foo {
  bar: true,
  ..Default::default()
} 

类似地,我们可以使用类型为PhantomData的私有字段来表示"此 struct 在future 版本中可能有更多字段"的 idea .

但是如果我们把这两个习语结合起来,我们会得到一个错误:

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 {
   |>     ^

从逻辑上讲,我认为这应该是可行的,因为我们只更新公共字段,这将是一个有用的习惯用法.另一种 Select 是支持以下内容:

Foo::new()
  .set_bar(true)

...这会让几十个字段变得单调乏味.

我如何解决这个问题?

推荐答案

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.

当然,如果用户 Select 使用__named字段,他们不会收到任何警告或任何东西,但__清楚地表明了意图.如果需要警告,可以使用#[deprecated].

Rust相关问答推荐

为什么在Rust struct 中只允许最后一个字段具有动态大小的类型

为什么父作用域中的变量超出了子作用域

无法将记录器向下转换回原始 struct

如何在Rust中将选项<;选项<;字符串>;转换为选项<;选项&;str>;?

如何实现Serde::Ser::Error的调试

AXUM一路由多个不包括URL的参数类型

如何在Rust中基于字符串 Select struct ?

链表堆栈溢出

用于实现获取 struct 体 id 的特征规范

类型生命周期绑定的目的是什么?

如何执行数组文字的编译时串联?

为什么 Rust 需要可变引用的显式生命周期而不是常规引用?

如何以与平台无关的方式将OsString转换为utf-8编码的字符串?

str 和 String 的 Rust 生命周期

从光标位置旋转精灵

在 Rust 中,为什么整数溢出有时会导致编译错误或运行时错误?

如何展平以下嵌套的 if let 和 if 语句?

将 `&T` 转换为新类型 `&N`

如何获得对数组子集的工作可变引用?

在 FFI 的上下文中,未初始化是什么意思?