《Effective Rust》第 6 条:拥抱 newtype 模式

  • King
  • 更新于 2024-06-15 12:08
  • 阅读 918

第6条:拥抱newtype模式第1条描述了元组结构体,它的字段没有名字,而是通过数字(self.0)来引用。本条着重介绍的是,只包含一个类型的元组结构体。它是一个新的类型,可以包含和内置类型一样的值。在Rust中,这个模式非常普遍,它叫做:newtype模式。newtype模

第 6 条:拥抱 newtype 模式

[第 1 条]描述了元组结构体,它的字段没有名字,而是通过数字(self.0)来引用。本条着重介绍的是,只包含一个类型的元组结构体。它是一个新的类型,可以包含和内置类型一样的值。在 Rust 中,这个模式非常普遍,它叫做:newtype 模式。

newtype 模式的最简单用法,是在类型原有行为的基础上,提供[额外的语义]。想象有一个将卫星送往火星的项目。[^1]这是一个大项目,不同的团队已经构建了项目的不同部分。其中一个小组负责火箭引擎的代码:

/// 点燃推进器。返回产生的脉冲,单位为磅/秒。
pub fn thruster_impulse(direction: Direction) -> f64 {
    // ...
    return 42.0;
}

另一个团队负责惯性导航系统:

/// 根据提供的推力(单位:牛顿/秒)更新轨迹模型。
pub fn update_trajectory(force: f64) {
    // ...
}

最终,结合这些不同部分:

let thruster_force: f64 = thruster_impulse(direction);
let new_direction = update_trajectory(thruster_force);

糟糕。[^2]

Rust 有类型别名的特性,让不同的团队能够更清楚地表达他们的意图:

/// 推力的单位。
pub type PoundForceSeconds = f64;

/// 点燃推进器。返回产生的脉冲。
pub fn thruster_impulse(direction: Direction) -> PoundForceSeconds {
    // ...
    return 42.0;
}
/// 推力的单位。
pub type NewtonSeconds = f64;

/// 更新冲力轨迹模型。
pub fn update_trajectory(force: NewtonSeconds) {
    // ...
}

然而,实际上类型别名只是文档:它们比前面的文档注释有更强的提示,但不能阻止 PoundForceSeconds 值被使用在希望使用 NewtonSeconds 值的地方。

let thruster_force: PoundForceSeconds = thruster_impulse(direction);
let new_direction = update_trajectory(thruster_force);

再次出现问题了。

这就是 newtype 模式能带来帮助的地方

/// 推力的单位。
pub struct PoundForceSeconds(pub f64);

/// 点燃推进器。返回产生的脉冲。
pub fn thruster_impulse(direction: Direction) -> PoundForceSeconds {
    // ...
    return PoundForceSeconds(42.0);
}
/// 推力的单位。
pub struct NewtonSeconds(pub f64);

/// 更新冲力轨迹模型。
pub fn update_trajectory(force: NewtonSeconds) {
    // ...
}

如名称所示,newtype 是一个新类型。因此,当型不匹配时,编译器会报错。在这里,我们尝试将 PoundForceSeconds 值传递给期望使用 NewtonSeconds 值的地方:

let thruster_force: PoundForceSeconds = thruster_impulse(direction);
let new_direction = update_trajectory(thruster_force);

let new_direction = update_trajectory(thruster_force);
error[E0308]: mismatched types
  --> src/main.rs:76:43
   |
76 |     let new_direction = update_trajectory(thruster_force);
   |                         ----------------- ^^^^^^^^^^^^^^ expected
   |                         |        `NewtonSeconds`, found `PoundForceSeconds`
   |                         |
   |                         arguments to this function are incorrect
   |
note: function defined here
  --> src/main.rs:66:8
   |
66 | pub fn update_trajectory(force: NewtonSeconds) {
   |        ^^^^^^^^^^^^^^^^^ --------------------
help: call `Into::into` on this expression to convert `PoundForceSeconds` into
      `NewtonSeconds`
   |
76 |     let new_direction = update_trajectory(thruster_...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
King
King
0x56af...a0dd
擅长Rust/Solidity/FunC/Move开发