专栏简介
本专栏是优质Rust技术专栏,推荐精通一门技术栈的蟹友,不建议完全无计算机基础的同学
感谢Rust圣经开源社区的同学,为后来者提供了非常优秀的Rust学习资源
本文使用:
- 操作系统macOS Sonoma 14 / Apple M1
- 编译器:Rustc & Cargo
感谢一路相伴的朋友们,感谢你们的支持 ^ _ ^
Rust教程:How to Rust-基本类型
更新记录
2024.3.24 发布文章
前言
Rust中的整型怎么写?整型溢出会怎么样?浮点型呢?NaN是什么?如何进行类型转换?
整型
Rust的类型系统与其他编程语言有许多相似之处,但Rust对类型的处理有其独特之处。在Rust中,整数类型的符号大小直接体现在类型名称中,这有助于清晰地表达每个类型的取值范围。
Rust的整数类型如下:
Rust整数类型
长度有符号类型无符号类型8位i8u816位i16
u16
32位i32u3264位i64
u64
128位i128u128视架构isizeusize
类型定义的形式统一为:有无符号 + 大小(位数)。无符号数表示数字只能取正数和0,而有符号则表示数字可以取正数、负数以及0。就像我们在纸上写数字一样,当需要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字在Rust中是以补码形式存储的。
在Rust编程语言中,
isize
和
usize
是两个与平台相关的整数类型。它们的大小取决于目标机器的指针大小。
isize
是一个有符号整数类型,其大小与机器的字大小相同。在32位系统上,
isize
通常是32位;在64位系统上,
isize
通常是64位。
usize
则是相应的无符号版本。
整型溢出
关于整型溢出,Rust有一些特别的规则。当在debug模式编译时,Rust会检查整型溢出,并在发现溢出时使程序panic(即崩溃)。然而,在release模式下编译时,Rust不检测溢出,而是按照补码循环溢出的规则处理。这意味着大于该类型最大值的数值会被转换成该类型能够支持的对应的最小值。例如,在
u8
类型中,256会变成0,257会变成1,以此类推。
为了显式处理可能的溢出,Rust标准库为原始数字类型提供了一系列方法:
- 使用
wrapping_*
方法在所有模式下都按照补码循环溢出规则处理,例如wrapping_add
。 - 如果使用
checked_*
方法时发生溢出,则返回None
值。 - 使用
overflowing_*
方法返回该值和一个指示是否存在溢出的布尔值。 - 使用
saturating_*
方法,可以限定计算后的结果不超过目标类型的最大值或不低于最小值。
例如:
let result1 = (250_u8).wrapping_add(10); // 结果是 4
let result2 = (120_i8).wrapping_add(10); // 结果是 -126
let result3 = (300_u16).wrapping_mul(800); // 结果是 43392
let result4 = (-100_i8).wrapping_sub(100); // 结果是 56
在这些例子中,
wrapping_*
方法用于在溢出时按照补码循环溢出的规则进行处理,而不是使程序崩溃。然而,需要注意的是,依赖这种默认行为的代码通常被认为是错误的,因为它可能导致程序中的逻辑错误或数据损坏。在编写涉及整数运算的代码时,应该仔细考虑如何处理可能的溢出情况,并使用适当的方法来检查或处理它们
浮点型
浮点类型数字是带有小数点的数值,在Rust中,主要有两种浮点类型:
f32
和
f64
,分别对应32位和64位大小的浮点数。默认情况下,Rust使用
f64
作为浮点类型,因为在现代CPU上,尽管
f32
和
f64
的速度相差无几,但
f64
提供了更高的精度。
let variable_float = 2.0;
let variable_f32: f32 = 3.0;
变量
variable_float
的类型是默认的
f64
,而变量
variable_f32
的类型则是显式声明的
f32
。当然,你也可以显式声明
f64
类型的变量,例如:
let variable_f64: f64 = 4.0;
使用浮点数时,如果不谨慎,可能会带来一些潜在的危险。这主要有两个原因:
首先,浮点数通常是用来近似表达你想要的数值。请注意,这里的“近似表达”是因为浮点数类型是基于二进制实现的,而我们通常使用的数字则是基于十进制的。例如,数值
0.1
在二进制中无法精确表示,这导致了一定的歧义。尽管浮点数能够代表真实的数值,但由于其底层格式的限制,它通常受限于定长的浮点数精度。如果你需要表达完全精确的真实数字,则需要使用具有无限精度的数学库。
其次,浮点数在某些特性上是反直觉的。虽然可以使用
>
、
>=
等运算符对浮点数进行比较,但在某些场景下,这种直觉上的比较特性可能会导致错误。
f32
和
f64
上的比较运算实现了
std::cmp::PartialEq
特性,但并没有实现
std::cmp::Eq
特性,而后者在其他数值类型上都有定义。
来看个例子
fn main()
{
assert_eq!(0.1 + 0.2, 0.3); // 这行代码会触发 panic,因为二进制精度问题导致 0.1 + 0.2 不严格等于 0.3
}
上述代码中,第三行是一个断言,断言
0.1 + 0.2
的结果就是
0.3
。然而,由于二进制精度的问题,
0.1 + 0.2
并不严格等于
0.3
,它们之间可能存在小数点后某位的误差,这与大多数编程语言中的浮点数行为是一致的。
NaN
对于数学上未定义的结果,如负数开平方根,Rust的浮点数类型会使用NaN(Not a Number)来处理这些情况。任何与NaN进行交互的操作都会返回NaN,并且NaN不能用于比较(如断言),这会导致程序崩溃。
例如,以下代码会产生NaN:
fn main()
{
let variable = (-1.1_f64).sqrt();
}
为了避免程序因NaN而崩溃,我们可以使用一些方法来检查数值是否为NaN,例如使用
.is_nan()
方法:
fn main()
{
let variable = (-1.1_f64).sqrt();
if variable.is_nan()
{
println!("NaN")
}
}
使用
.is_nan()
可以帮助我们安全地处理可能产生NaN的浮点数运算。
使用As进行类型转换
在Rust中,
as
关键字用于在原始类型(如
i64
、
f64
、
u64
、
char
等)之间进行类型转换。然而,需要注意的是,
as
关键字并不适用于复合类型,比如
String
或其他用户定义的类型。对于复合类型的转换,通常需要使用其他方法或函数。
例如,如果你有一个
i32
类型的变量,并希望将其转换为
f64
类型的浮点数,你可以使用
as
关键字来实现这一转换:
let num_i32: i32 = 42;
let num_f64: f64 = num_i32 as f64;
但是,
as
关键字的使用是有限制的,它只能在那些具有明确定义转换规则的类型之间起作用。如果尝试在不兼容的类型之间进行转换,Rust编译器会报错。例如,下面的代码尝试将一个
String
转换为
i32
,这是不允许的,因为
String
和
i32
之间没有直接的转换关系:
let str_num = "123".to_string();
let num_i32: i32 = str_num as i32; // 这行会报错,因为 String 不能直接转换为 i32
在上面的代码中,尝试使用
as
来转换
str_num
变量会导致编译错误,因为
String
类型不能直接用
as
关键字转换为
i32
类型。如果需要将字符串转换为整数,你需要使用其他方法,比如
parse
方法:
let str_num = "123";
let num_i32: i32 = str_num.parse().unwrap(); // 使用 parse 方法来将字符串转换为整数
在上面的代码中,我们使用了
parse()
方法,该方法尝试将字符串解析为相应的数值类型,并返回一个
Result
枚举,表示解析操作是否成功。使用
unwrap()
方法可以获取解析结果,但如果解析失败(比如字符串不是有效的数字表示),则会触发panic。在实际应用中,通常需要对
parse()
的返回值进行更健壮的错误处理。
因此,在使用
as
进行类型转换时,需要确保转换的类型之间具有明确定义的转换关系,并且不适用于复合类型或没有直接转换关系的类型。对于更复杂的类型转换需求,需要利用Rust提供的相应函数或方法。
结语
如果本文有任何问题欢迎在评论去指出,如果喜欢这篇文章,希望能点赞评论关注
如果你们身边有像你提起过这个领域的,或者希望可以和ta一起进步的,把这篇文章分享给ta吧
本文共3646字
本文参考文献
Rust圣经
文心一言
菜鸟求助,使用 isize 或 usize 作为索引类型更加灵活 - Rust语言中文社区
在 Rust 中处理整数溢出
版权归原作者 Cat Bayi 所有, 如有侵权,请联系我们删除。