4 个版本
0.1.3 | 2024 年 5 月 17 日 |
---|---|
0.1.2 | 2024 年 5 月 10 日 |
0.1.1 | 2024 年 5 月 10 日 |
0.1.0 | 2024 年 5 月 6 日 |
106 在 编程语言 中排名
每月 下载 180 次
58KB
1K SLoC
轻量级带范围的整数
带范围的整数用于稳定版 Rust 编译器,无依赖项且无不安全代码。
为什么
将整数变量限制在特定范围的概念来自像 Ada 这样的稳固语言,如 Ada,通过防止意外的越界垃圾值来帮助创建可靠的软件。
在 Rust 中,我们有 ranged_integers 包,它非常强大,具有自动类型大小和自动区间极限扩展;然而,它目前需要一个 Nightly 编译器,并启用一些实验性编译器功能,因为它需要更多对常量表达式和泛型的控制。
虽然更高效的编译时自动化是受欢迎的,但在大多数情况下,一些运行时检查就足够了。事实上,即使在 Ada 中,范围类型也主要是运行时检查的,除了范围声明和第一次初始化。如果运行时检查对 Ada 的高安全标准来说足够好,那么它可能对您的用例也足够好,但这取决于您自己决定。
与 Rust 的 ranged_integers 包相比,这个库具有更少的编译自动化功能,但它可以在稳定版编译器版本上运行,无依赖项,代码库非常小。
用法
范围类型保证包含的数字符合从 MIN 到 MAX 的范围,包括极值。MIN 必须小于 MAX,否则将拒绝编译。
库提供范围类型,声明如下
RangedU16<MIN, MAX, OpMode>
其中
RangedU16
表示一个内部将数字保存为 u16(无符号,16位)的范围类型MIN
是区间的下限MAX
是区间的上限OpMode
选择当数字超出范围时的行为
use light_ranged_integers::{RangedU16, op_mode::Panic};
// Create a value of 3 in a range from 1 to 6, extremes included
let n1 = RangedU16::<1,6, Panic>::new(3);
// This is checked at compile time
let n1 = RangedU16::<1,6>::new_const::<3>();
让我们来看一个更实际的用例:让我们表示一个非闰年的日子。每个月的天数不同,我们想要确保不能接受无效的日子数字,从而保证日-月组合始终有效。
use light_ranged_integers::RangedU8;
enum DayInYear
{
January(RangedU8<1,31>),
February(RangedU8<1,28>),
March(RangedU8<1,31>),
April(RangedU8<1,30>),
May(RangedU8<1,31>),
June(RangedU8<1,30>),
July(RangedU8<1,31>),
August(RangedU8<1,31>),
September(RangedU8<1,30>),
October(RangedU8<1,31>),
November(RangedU8<1,30>),
December(RangedU8<1,31>)
}
// Range validity is done at compile-time,
// but the check of the number 10 fitting it is done
// at runtime.
// Let's put April 10th
let day = DayInYear::April(
RangedU8::new(10)
);
// As we know the value at compile time,
// we may want to check and guarantee the number fits
// the range at compile time too
let day = DayInYear::April(
RangedU8::new_const::<10>()
);
模式
当您声明一个范围类型时,您可以选择在数字超出范围时应用的行为。目前有三种模式: Panic
、Clamp
和 Wrap
。
Panic
Panic 模式是默认模式。如果数字超出范围,程序会崩溃。
use light_ranged_integers::{RangedU16, op_mode::Panic};
// Create a ranged u16, interval [1,6], in Panic mode.
let n1 = RangedU16::<1,6, Panic>::new(5);
// As Panic mode is the default, you can also initialize using only the range
let n1 = RangedU16::<1,6>::new(5);
let res = n1+5;// This line will panic, as 5+5=10 is out of range
Clamp
在 Clamp 模式下,不适应区间的数字会被夹到最近的极端值。例如,当您尝试将 10 适配到 [1, 6] 区间时,它会夹到 6。
use light_ranged_integers::{RangedU16, op_mode::Clamp};
// Create a ranged u16, interval [1,6], in Clamp mode.
let n1 = RangedU16::<1,6, Clamp>::new(3);
assert_eq!(n1,3);
assert_eq!(n1+400, 6);
// Here 10 is clamped to a 6 to fit into range
let n2 = RangedU16::<1,6, Clamp>::new_adjust(10);
assert_eq!(n2,6);
// We're in clamp mode, so 3+6=9 is clamped at 6 automatically
assert_eq!(n1+n2, 6)
Wrap
Wrap 模式。
在这个模式下,超出范围的数字会被包裹以适应区间。
use light_ranged_integers::{RangedU16, op_mode::Wrap};
// Adding +1 will wrap the number at the beginning of the interval
let n1 = RangedU16::<3,6, Wrap>::new(6);
assert_eq!(n1+1, 3);
// Subtract 1 from MIN. Returns MAX
let n2 = RangedU16::<3,6,Wrap>::new(3);
assert_eq!(n2-1, 6);
// one full interval cycle
assert_eq!(n2-4, n2);
您也可以在初始化范围时包裹新值
use light_ranged_integers::{RangedU16, op_mode::Wrap};
let n = RangedU16::<3,6,Wrap>::new_adjust(7);
assert_eq!(n, 3);
额外的安全限制
该库提供了一些额外的安全限制。
不可变范围
范围限制不会自动扩展或收缩,因此您始终知道范围是什么。
use light_ranged_integers::{RangedU16, op_mode::Clamp};
let n1 = RangedU16::<1,6, Clamp>::new(3);
let n2 = RangedU16::<1,6, Clamp>::new(6);
// Expand the limit of each operand to 1-12
// Then 3+6=9 fits the range and 9 is not clamped
assert_eq!(
n1.limit::<1,12>()
+
n2.limit::<1,12>(),
9
);
结果范围
如果我们添加一个范围 [0,3] 的值和一个范围 [-1,6] 的值,输出范围应该是什么?根据情况,开发者可能想要前者、后者,或者完全不同的一个。由于我们无法假设所需的范围,因此编译时禁止在具有不同范围的值之间进行操作。
use light_ranged_integers::RangedU8;
// Does NOT compile, different ranges
// one is [0,1], the other is [0,2]
// we can't assume an output range
RangedU8::<0,1>::new(0) + RangedU8::<0,2>::new(0)
不同的 OpMode
尝试在设置在不同 OpMode 中的范围类型之间进行操作时,也适用同样的规则。例如,如果一个操作数处于 Clamp 模式,而另一个处于 Panic 模式,当数字超出范围时,代码应该怎么做?结果 OpMode 应该是前者还是后者?
由于我们无法假设所需的 OpMode,最好根本不进行编译。
use light_ranged_integers::{RangedU8, op_mode::{Clamp, Panic}};
// Does NOT compile:
// First operand is Panic mode,
// second operand is in Clamp mode
RangedU8::<0, 1, Panic>::new(0)
+
RangedU8::<0, 1, Clamp>::new(0);
版权
本软件是免费和开源软件,根据其许可证定义,这不是公共领域,请确保遵守许可证条款。您可以在 COPYING 文件中找到许可证文本。
版权 © 2024 Massimo Gismondi
本程序是自由软件:您可以在自由软件基金会发布的 GNU 通用公共许可证的条款下重新分发和/或修改它,无论是许可证的第 3 版,还是您选择的任何较新版本。
本程序是根据希望它将是有用的目的进行分发的,但没有任何保证;甚至没有对适销性或对特定目的适用性的暗示保证。有关详细信息,请参阅 GNU 通用公共许可证。
您应该已收到此程序的 GNU 通用公共许可证副本。如果没有,请参阅 https://www.gnu.org/licenses/。
依赖关系
此项目没有运行时依赖。
它有两个开发依赖项,仅用于测试