DataType
本文主要介绍rust
中的数据类型。总的来说可以分为两类:基本
类型和复合
类型还有自定义
类型。
基本类型
基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:
- 数值类型: 有符号整数
(i8, i16, i32, i64, isize)
、无符号整数(u8, u16, u32, u64, usize)
、浮点数(f32, f64)
、以及有理数、复数。 - 字符串:字符串字面量和字符串切片
&str
。 - 布尔类型:
true
和false
。 - 字符类型: 表示单个
Unicode
字符,存储为4
个字节。 - 单元类型: 即
()
,其唯一的值也是()
。
数值类型
数值分为以下两种情况:
整数类型
TIP
每个有符号类型规定的数字范围是 ~ ,其中n
是该定义形式的位长度。因此i8
可存储数字范围是 ~ ,即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ ,所以u8
能够存储的数字为 0 ~ ,即 0 ~ 255。
整数是没有小数部分的数字。之前使用过的i32
类型,表示有符号的 32
位整数(i
是英文单词integer
的首字母,与之相反的是u
,代表无符号unsigned
类型)。下表显示了Rust
中的内置的整数类型:
长度 | 有符号名称 | 无符号名称 |
---|---|---|
8位 | i8 | u8 |
16位 | i16 | u16 |
32位 | i32 | u32 |
64位 | i64 | u64 |
128位 | i128 | u128 |
视架构而定 | isize | usize |
类型定义的形式统一为:有无符号 + 类型大小(位数)。无符号数表示数字只能取正数,而有符号则表示数字既可以取正数又可以取负数。就像在纸上写数字一样:当要强调符号时,数字前面可以带上正号或负号;然而,当很明显确定数字为正数时,就不需要加上正号了。有符号数字以补码形式存储。
此外isize
和usize
类型取决于程序运行的计算机CPU
类型: 若CPU
是32
位的,则这两个类型是32
位的,同理,若CPU
是64
位,那么它们则是64
位。
这么多类型,有没有一个简单的使用准则?答案是肯定的, Rust 整型默认使用i32
,例如let i = 1
,那i
就是i32
类型,因此你可以首选它,同时该类型也往往是性能最好的。isize
和usize
的主要应用场景是用作集合的索引。
整型溢出
假设有一个u8
,它可以存放从0
到255
的值。那么当你将其修改为范围之外的值,比如256
则会发生整型溢出。关于这一行为Rust
有一些有趣的规则:当在debug
模式编译时Rust
会检查整型溢出,若存在这些问题,则使程序在编译时panic
(崩溃,Rust
使用这个术语来表明程序因错误而退出)。
浮点类型
浮点类型数字 是带有小数点的数字,在Rust
中浮点类型数字也有两种基本类型:f32
和f64
,分别为 32 位和 64 位大小。默认浮点类型是f64
,在现代的CPU
中它的速度与f32
几乎相同,但精度更高。
浮点数根据 IEEE-754 标准实现。f32 类型是单精度浮点型,f64 为双精度。
浮点数计算与其他语言都存在精度的问题,如0.1
你需要遵守以下准则:
- 避免在浮点数上测试相等性
- 当结果在数学上可能存在未定义时,需要格外的小心。
NaN 对于数学上未定义的结果,例如对负数取平方根-42.1.sqrt()
,会产生一个特殊的结果:Rust 的浮点数类型使用NaN
(not a number)来处理这些情况。
所有跟NaN
交互的操作,都会返回一个NaN
,而且NaN
不能用来比较
出于防御性编程的考虑,可以使用is_nan()
等方法,可以用来判断一个数值是否是NaN
:
fn main() {
let x = (-42.0_f32).sqrt();
if x.is_nan() {
println!("未定义的数学行为")
}
}
fn main() {
let x = (-42.0_f32).sqrt();
if x.is_nan() {
println!("未定义的数学行为")
}
}
数字运算
Rust
支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算:
fn main() {
// 加法
let sum = 5 + 10;
// 减法
let difference = 95.5 - 4.3;
// 乘法
let product = 4 * 30;
// 除法
let quotient = 56.7 / 32.2;
// 求余
let remainder = 43 % 5;
}
fn main() {
// 加法
let sum = 5 + 10;
// 减法
let difference = 95.5 - 4.3;
// 乘法
let product = 4 * 30;
// 除法
let quotient = 56.7 / 32.2;
// 求余
let remainder = 43 % 5;
}
位运算
Rust的运算基本上和其他语言一样
运算符 | 说明 |
---|---|
& 位与 | 相同位置均为1时则为1,否则为0 |
| 位或 | 相同位置只要有1时则为1,否则为0 |
^ 异或 | 相同位置不相同则为1,相同则为0 |
! 位非 | 把位中的0和1相互取反,即0置为1,1置为0 |
<< 左移 | 所有位向左移动指定位数,右位补零 |
>> 右移 | 所有位向右移动指定位数,左位补零 |
fn main() {
// 二进制为00000010
let a:i32 = 2;
// 二进制为00000011
let b:i32 = 3;
println!("(a & b) value is {}", a & b);
println!("(a | b) value is {}", a | b);
println!("(a ^ b) value is {}", a ^ b);
println!("(!b) value is {} ", !b);
println!("(a << b) value is {}", a << b);
println!("(a >> b) value is {}", a >> b);
let mut a = a;
// 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)
a <<= b;
println!("(a << b) value is {}", a);
}
fn main() {
// 二进制为00000010
let a:i32 = 2;
// 二进制为00000011
let b:i32 = 3;
println!("(a & b) value is {}", a & b);
println!("(a | b) value is {}", a | b);
println!("(a ^ b) value is {}", a ^ b);
println!("(!b) value is {} ", !b);
println!("(a << b) value is {}", a << b);
println!("(a >> b) value is {}", a >> b);
let mut a = a;
// 注意这些计算符除了!之外都可以加上=进行赋值 (因为!=要用来判断不等于)
a <<= b;
println!("(a << b) value is {}", a);
}
序列(Range)
Rust
提供了一个非常简洁的方式,用来生成连续的数值,例如1..5
,生成从1
到4
的连续数字,不包含5
;1..=5
,生成从1
到5
的连续数字,包含5
,它的用途很简单,常常用于循环中:
for i in 1..=5 {
println!("{}",i);
}
for i in 1..=5 {
println!("{}",i);
}
有理数和复数
Rust
的标准库相比其它语言,准入门槛较高,因此有理数和复数并未包含在标准库中: 采用社区开发的数值库:num。
- 在
Cargo.toml
中的[dependencies]
下添加一行num = "0.4.0"
- 将
src/main.rs
文件中的main
函数替换为下面的代码 - 运行`cargo run``
use num::complex::Complex;
fn main() {
let a = Complex { re: 2.1, im: -1.2 };
let b = Complex::new(11.1, 22.2);
let result = a + b;
println!("{} + {}i", result.re, result.im)
}
use num::complex::Complex;
fn main() {
let a = Complex { re: 2.1, im: -1.2 };
let b = Complex::new(11.1, 22.2);
let result = a + b;
println!("{} + {}i", result.re, result.im)
}
字符(注意 不是字符串)
Rust
的字符不仅仅是ASCII
,所有的Unicode
值都可以作为Rust
字符,包括单个的中文、日文、韩文、emoji 表情符号等等,都是合法的字符类型。Unicode
值的范围从U+0000 ~ U+D7FF 和 U+E000 ~ U+10FFFF
。(字符类型占用4
个字节与Unicode
相等)
fn main() {
let x = '中';
println!("字符'中'占用了{}字节的内存大小",std::mem::size_of_val(&x));
}
fn main() {
let x = '中';
println!("字符'中'占用了{}字节的内存大小",std::mem::size_of_val(&x));
}
布尔(bool)
Rust 中的布尔类型有两个可能的值:true 和 false,布尔值占用内存的大小为 1 个字节:
fn main() {
let t = true;
let f: bool = false; // 使用类型标注,显式指定f的类型
if f {
println!("这是段毫无意义的代码");
}
}
fn main() {
let t = true;
let f: bool = false; // 使用类型标注,显式指定f的类型
if f {
println!("这是段毫无意义的代码");
}
}
单元类型
单元类型就是()
,对,你没看错,就是()
,唯一的值也是()
比如:
- 函数
main()
. - 你可以用
()
作为map
的值,表示我们不关注具体的值,只关注key
。这种用法和Go
语言的struct{}
类似,可以作为一个值用来占位,但是完全不占用任何内存。
总结
Rust
类型转换必须是显式的(js
默认是隐式的).Rust
的数值上可以使用方法。例如你可以用以下方法来将13.14
取整:13.14_f32.round()
,在这里我们使用了类型后缀,因为编译器需要知道13.14
的具体类型.