Rust - RFC 2700 assoc_int_consts

Rust - RFC 2700 assoc_int_consts

  • 功能名称: assoc_int_consts
  • 启动日期: 2019-05-13
  • RFC PR: rust-lang/rfcs#2700
  • Rust Issue: rust-lang/rust#68490

摘要

为标准库中的数值类型添加相关的关联常量,并考虑逐步弃用对应的(最初仅为临时过渡)原始数值模块及其相关函数。

动机

所有带界限的整数类型的编程语言都会为最大值和最小值提供数值常量。在 Rust 中,这些常量在 1.0 发布前夜被稳定化(实际上是在 2015 年 4 月 1 日 1.0-beta 分支前一天),但这样做有一些已知的不理想之处。特别是,当时还没有实现关联常量(实际上关联常量在 1.0-beta 后一个月、1.0-stable 前两周才落地),所以 12 种数值类型各自都有一个顶层模块,模块内容仅包括这些常量(所有相关的非常量则直接定义在类型的 inherent impl 上)。但在 1.0-beta 更晚些时候,人们意识到这种方案对引用如 c_int 这类类型别名的常量不友好,因为类型别名可以访问 inherent impl,但不能访问仅仅同名的模块;因此,有个紧急 PR 又加上了冗余的 max_valuemin_value inherent 方法作为权宜之计。PR 本身也承认这种做法不理想:

虽然把这些做成方法有点遗憾,但当我们能提供 inherent 关联常量时,这些方法可以被弃用。[aturon, 2015年4月1日]

与此同时,实现关联常量的作者也遗憾自己错过了最后期限:

@nikomatsakis 最初希望赶在 beta 前合并这个功能,这样就能去掉 Int/Float 里所有常量相关的方法和模块,全部用 inherent impl 的常量。但 #23549 先合并并稳定了一堆常量,这个没赶上,唉。[quantheory, 2015年4月1日]

预见到这种情况,有人在 RFC 仓库提了 issue,建议把这些模块内容迁移到关联常量上:

我觉得把常量迁到类型并弃用 u8、u16 等模块算是轻微的 breaking change。完全删掉这些模块我倒是乐见,但会破坏所有用到它们的代码。[petrochenkov, 2015年4月29日]

事实上,这一方案如此显而易见,最初关于关联项的 RFC 就以数值常量为关联常量功能的唯一动机示例:

例如,当前 Rust 包含多种数值 trait,比如 Float,必须把常量暴露为静态函数 […] 关联常量能让常量直接属于 trait

尽管大家都很清楚应该怎么做,Rust 1.0 发布后却一直没空处理。现在,在 Rust 四周年前夕,我们重新审视这一问题。我们建议弃用所有上述函数和常量,转而在对应类型上定义关联常量,并进一步弃用所有直接存在于 i8i16i32i64i128isizeu8u16u32u64u128usizef32f64 模块中的常量。这样做的好处有:

  1. 与语言其余部分保持一致。正如上面引用所示,关联常量自一开始就是表达这些概念的自然方式,这样做也最符合直觉。

  2. 文档简洁。在标准库 API 文档首页上,60 个模块里有 12 个(占 20%)是这些只包含两个常量的数值模块。随着新数值类型增加,这个数字只会增加(如 i128u128)。虽然弃用的模块不能轻易从 std 移除,但可以从文档中隐藏,让文档更清爽易用。

  3. 初学者友好。初学者发现有两种方式做同一件事会疑惑“为什么”,而答案其实很无聊(甚至有点尴尬)。而且“应该用哪种”也说不清,目前两种都不更地道。弃用项可以从文档中隐藏,减少新手困惑。

  4. 消除类型与同名模块的歧义。当前如果导入整数模块并同时用类型的常量和方法,代码看着很迷惑:

use std::u32;
assert_eq!(u32::MAX, u32::max_value());

这种类型遮蔽模块的行为连资深 Rust 程序员也会感到意外,而标准库还鼓励这种用法就更奇怪了。改成关联常量后,可以去掉所有与基础类型同名的模块。

  1. 消灭恼人的小坑。连有经验的 Rust 程序员也会因此踩坑咒骂,时刻被 1.0 的古怪遗留提醒。去掉这些遗留能让 Rust 更加愉快。

指南级解释

  1. 在标准库相关类型上添加如下关联常量,定义取自原模块级常量:

    • i8::{MAX, MIN}
    • i16::{MAX, MIN}
    • i32::{MAX, MIN}
    • i64::{MAX, MIN}
    • i128::{MAX, MIN}
    • isize::{MAX, MIN}
    • u8::{MAX, MIN}
    • u16::{MAX, MIN}
    • u32::{MAX, MIN}
    • u64::{MAX, MIN}
    • u128::{MAX, MIN}
    • usize::{MAX, MIN}
    • f32::{DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX}
    • f64::{DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX}
  2. 将下列模块级常量重定义为上一步新加的关联常量:

    • std::i8::{MIN, MAX}
    • std::i16::{MIN, MAX}
    • std::i32::{MIN, MAX}
    • std::i64::{MIN, MAX}
    • std::i128::{MIN, MAX}
    • std::isize::{MIN, MAX}
    • std::u8::{MIN, MAX}
    • std::u16::{MIN, MAX}
    • std::u32::{MIN, MAX}
    • std::u64::{MIN, MAX}
    • std::u128::{MIN, MAX}
    • std::usize::{MIN, MAX}
    • std::f32::{DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX}
    • std::f64::{DIGITS, EPSILON, INFINITY, MANTISSA_DIGITS, MAX, MAX_10_EXP, MAX_EXP, MIN, MIN_10_EXP, MIN_EXP, MIN_POSITIVE, NAN, NEG_INFINITY, RADIX}
  3. 在未来某一时机(见下文“未决问题”)弃用第 2 步中的条目,并同时弃用如下关联函数:

    • i8::{min_value, max_value}
    • i16::{min_value, max_value}
    • i32::{min_value, max_value}
    • i64::{min_value, max_value}
    • i128::{min_value, max_value}
    • isize::{min_value, max_value}
    • u8::{min_value, max_value}
    • u16::{min_value, max_value}
    • u32::{min_value, max_value}
    • u64::{min_value, max_value}
    • u128::{min_value, max_value}
    • usize::{min_value, max_value}
  4. 第 3 步后,以下模块将从 std 文档首页隐藏,因为它们已无非弃用项:std::{i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize}std::{f32, f64} 不适用,见 Alternatives)。

缺点

  1. 会产生弃用警告,不过很容易修正。
  2. 由于关联项不能直接导入,类似 use std::i32::MAX; foo(MAX, MAX); 的代码需改为 foo(i32::MAX, i32::MAX),略微啰嗦。但考虑到 stdlib 有很多 MAXMIN,未加前缀常量反而容易混淆,本应避免。实在需要可用 const MAX: i32 = i32::MAX; 代替。

未决问题

我们应该多长时间后发出弃用警告?最极端可以等到下一个 Rust edition,只有选择新 edition 的人才会看到弃用警告,这样只影响主动 opt-in 的用户,且这只是弃用警告,远比普通 edition 变更温和。无论等待多久,作者认为最终都应弃用,不能让三种方式并存。虽然有经验的用户会自然优先用本 RFC 提议的新方式,但对新手而言,三种方式并存会带来教学困扰,我们应明确标注推荐做法。

备选方案

  • 与 12 个整数模块不同,两个浮点模块不会因本 RFC 全部弃用,因为 std::f32std::f64 还包含 consts 子模块,里面有更多数学常量(类似其他语言的 std::math)。作者认为数学常量与机器常量分开未必有先例,例如现有 f32/f64 的相关方法本身就混合了数学和机器相关的功能。不过,虽有早期版本建议连 std::{f32, f64}::consts 也一起弃用,当前版本已不再这样建议,因为有些反对意见,且整数模块的变化本身就足够有价值。如果需要,未来可再专门为此写 RFC。另一种方案是把 {f32, f64} 中所有常量也做成关联常量,这样所有与基础类型同名的模块都能从 std 移除。或者只针对整数模块,浮点部分留待以后处理,这样可保证整数部分先行落地,不被浮点问题拖累。

  • 也可以先加新关联常量而不立刻弃用旧项。但那样就有种方式做同一件事,没有弃用警告也没人知道推荐哪种。作者认为此处完全不存在弃用警告的坏处,反而利于规范用法(参考上文“缺点”)。

原文地址:https://rust-lang.github.io/rfcs/2700-associated-constants-on-ints.html

转载请说明出处内容投诉
CSS教程网 » Rust - RFC 2700 assoc_int_consts

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买