带有私有字段的 Rust impl 默认特征

Rust impl default trait with private fields

提问人:Richard Howell-Peak 提问时间:8/31/2020 最后编辑:pretzelhammerRichard Howell-Peak 更新时间:8/9/2023 访问量:7583

问:

当我进行这种设置时,我遇到错误:

default_test.rs:

mod default_mod;

use default_mod::Point;

fn main() {
    let _p1 = Point::new();
    let _p2: Point = Point {
        z: 1,
        ..Default::default()
    };
}

default_mod.rs:

pub struct Point {
    x: i32,
    y: i32,
    pub z: i32,
}

impl Point {
    pub fn new() -> Self {
        Point { x: 0, y: 0, z: 0 }
    }
}

impl Default for Point {
    fn default() -> Self {
        Point { x: 0, y: 0, z: 0 }
    }
}

这给出了编译器错误:

default_test.rs:9:7
  |
9 |     ..Default::default()
  |       ^^^^^^^^^^^^^^^^^^ field `x` is private

error[E0451]: field `y` of struct `default_mod::Point` is private

短版本 - 我有一个包含公共和私有字段的结构。我想用默认值初始化这个结构,但有时会覆盖它们。

我似乎无法修复此错误,也没有在互联网或文档上看到任何提及此类错误的内容。

这让我感到惊讶,因为我认为一个常见的用例是初始化一个结构,并且结构的某些成员将是私有的,因此您可以隐藏后面的实现细节和接口。

就我而言,私有字段是一个,因为我有一些逻辑需要在向量中添加或删除内容,所以我想将其设为私有以防止任何人搞砸数据结构。Vec

我在这里有什么选择?

rust 默认 私有 公共

评论

1赞 rodrigo 8/31/2020
将公共字段移动到单独的类型怎么样:然后执行 ?你的布局有点变化,但它应该有点工作。pub struct Data { pub x: i32, pub y: i32}; pub struct Point { pub data: Data, z: i32 };fn new_with_data(data: Data)
0赞 Richard Howell-Peak 8/31/2020
我其实挺喜欢这个主意的,我会考虑一下的

答:

3赞 Masklinn 8/31/2020 #1

我在这里有什么选择?

带有参数或构建器。new()

..struct只是进行功能更新的一种便捷方式,它不会绕过 ACL。在这里,由于您的结构具有私有字段,因此用户不能将其作为“裸”结构进行操作,他们必须将其视为在很大程度上不透明的类型。

评论

0赞 user4815162342 8/31/2020
我认为 OP 并不期望绕过 ACL,而是从明确指定的字段开始并更新,这是 OP 情况下的公共字段。..structstructz
0赞 Masklinn 8/31/2020
但这不是他们正在做的事情,他们正在做的是创建一个新的结构,复制旧的结构,同时替换字段的一个值。
0赞 user4815162342 8/31/2020
同意,只是 OP 对 struct-update 语法的解释与其实际含义不同。由于我记得当我第一次遇到 struct-update 语法与私有字段的交互时遇到过类似的困惑,所以我试图在我的答案中涵盖它。
6赞 user4815162342 8/31/2020 #2

这让我感到惊讶,因为我认为一个常见的用例是初始化一个结构,并且结构的某些成员将是私有的,因此您可以隐藏后面的实现细节和接口。

问题是 struct update 语法没有做你认为的那样。例如,本书显示以下代码:

let user2 = User {
    email: String::from("[email protected]"),
    username: String::from("anotherusername567"),
    ..user1
};

语法填充了我们尚未明确指定的字段,例如 . 后面可以跟着一个任意表达式,该表达式返回结构,这是起作用的地方,其含义与因为 a 是预期的相同。然而,脱糖保持不变,归结为分配单个字段,在这两种情况下都授予对私有字段的特殊访问权限。..user1Useractive: user1.active, signin_count: user1.signin_count..Default::default()User::default()User

为了回到您的示例,请执行以下代码:

let p = Point {
    z: 1,
    ..Default::default()
};

是句法糖:

let p = {
    let _tmp = Point::default();
    Point {
        x: _tmp.x, // assigning private field
        y: _tmp.y, // assigning private field
        z: 1,
    }
};

而不是预期的:

// NOT what happens
let p = {
    let _tmp = Point::default();
    p.z = 1; // no private assignment
    _tmp
};

我在这里有什么选择?

最惯用的选项是提供 的构建器。这也有点笨重1,所以如果你正在寻找一个简单的解决方案,你也可以手动使用和设置属性。struct update 语法与具有私有字段的结构不兼容,对您的类型没有用处。PointPoint::default()z


1 虽然有像derive_buildertyped-builderbuilder-pattern 这样的板条箱可以消除一些苦差事。