基础数据类型和复合类型
基础类型 标量(scalar
)
标量的定义是:单独的对象、值。Rust提供了四种基本的标量:布尔型、字符型、整型、浮点型。
布尔型
Rust里的bool
有两个字面值:true
和false
,例:
1 | let mut fg = false; |
字符型
Rust的char
字面值用两个'
包围单个char
表示,与其他语言不同的是,Rust的char
大小为4Bytes,因为Rust里的char
编码格式为Unicode编码,意味着单个char
能存储单个字母、中文、日文、emoji等,例:
1 | let ch = 'a'; |
但是Rust的str
和String
不以char
为单位存储字符,而是按照UTF-8格式存储字符串。
整型(Integer
)
Rust中整型分有符号和无符号两类,下面表格中列出了Rust原生的整型:
长度 | 有符号 | 无符号 |
---|---|---|
1Byte | i8 |
u8 |
2Bytes | i16 |
u16 |
4Bytes | i32 |
u32 |
8Bytes | i64 |
u64 |
16Bytes | i128 |
u128 |
基于计算机架构 | isize |
uszie |
可能有人会问为什么原生的只有
i(2^(2+n))
,而没有i19
、i24
之类的。我从网上查到了较详细的解释,可参照Beginning_Rust:使用原始类型(第六章)(完+1)
这里重点说明一下isize
和uszie
,当编译目标为x32
架构时,其长度为32bit
,当编译目标是x64
架构时,其长度为64bit
。因为size
类型主要用于内存地址、位置、索引、记录长度(或相对偏移量)或大小。
运算符类型 | 运算符 |
---|---|
算术运算符 | + ,- ,* ,/ ,% |
关系运算符 | == ,!= ,< ,> ,<= ,>= |
位运算符 | & ,| ,^ ,! (按位非/按位取反),<< ,>> |
赋值运算符 | = ,{+ ,- ,* ,/ ,% }= ,{& ,| ,^ ,<< ,>> }= |
同许多语言一样,Rust的整型支持许多运算符操作,但与C/C++不同的是,Rust 没 有 自增(++)自减(--)运算符!另外,也不能对整型使用逻辑运算符(逻辑运算符只能用于bool
,要想使用就必须先将Integer
转成bool
,类似的,关系运算符两边的类型也必须相同)。
1 | fn main() { |
尝试编译上述代码,编译器会报错:
1 | | if a && true { |
至于为什么没有自增自减运算符,Rust FAQ(英|中)对此有较合理的答案,可自行参阅。
当然这在某些场景下会带来很大的麻烦(比如刷题),但请放轻松,除了需要将赋值分开写而不能连着写外,又有多大的损失呢?
(如果你是压行狂魔的话当我没说)
附加内容:整型溢出
Rust的整型溢出比较特殊:在debug编译运行时,如发生整型溢出,就会panic
(Rust术语,指程序抛出错误信息并停止运行),报错发生了整型溢出;在release编译运行时,则能正常运行而不panic
,此时,溢出的部分将会被消除,例如一个u8
为255
时,加上1
就会变成0
。
直接使用整型溢出的特性在Rust中被认为是一种错误用法,需要的话,请使用std::num::Wrapping
。
浮点型
Rust目前只有两个采用IEEE-754标准存储运算的原生浮点型:f32
和f64
。目前Rust并没有设计f16
、f128
(可能将来会有,消息不明),不过已经有第三方库(实验中)f128实现了f128
。
显式声明字面值类型
在某些情况下,可能会需要确定字面值的类型(因为默认的整型字面值类型为i32
,浮点字面值类型为f64
),这时可以在字面值两边添加一些字符以确定其类型:
1 | // 将123类型确定为`u8`,此时a的类型也会变成u8 |
普通类型强转
Rust提供隐式的普通类型强转,大致格式为:数据 as 目标类型
,例如:
1 | let i = 4.5 as i32; // i == 4 |
复合类型
数组
同其它语言一样,Rust也有数组,数组里的每个元素的类型必须相同。
声明一个数组,可使用多种方法:
1 | // 格式let xx: [元素类型; 数量] = ,数组类型可不写,让编译器推断。 |
访问数组中的元素可以直接使用下标访问,例如arr[0]
。
还有,Rust数组的元素数量必须是常数,因为编译器需要在编译时就能确定数组的大小,这样就能分配栈内存给数组以加快效率。如果你想使用动态数组,你可以使用Vec
(Vector),相对的,因为Vec
大小无法在编译时确定,Rust只能为其分配堆内存,其效率也会比数组更低。
元组(tuple
)
数组虽然很灵活,但当我们需要将一堆类型不同的数据放到一个类型中时数组就无能为力了。
为了解决这个问题,我们可以直接声明一个结构体,但更方便的方法是使用元组。
元组类型形如(T1, T2, T3,..., Tn)
,文字不好描述,下面我结合代码解释:
1 | // 声明一个(i32, f64) |
而访问一个元组可以通过使用以下方式访问:
1 | let tu: (true, '例', 12, 3.4); |
元组支持所有用Rust定义的类型,意味着所有Rust原生类型和自己定义的类型都能放入元组中。
回顾一下,1.3中我提到
...
main()
函数无返回值(返回值为()
)...
其中的()
元组是特殊的元组,()
元组又被称为单元类型(unit type
),字面值()
又被称为单元值(unit value
)(()
既可以视为类型,也可以视为字面值)。如果一个函数没有写返回值,编译器会自动补上返回()
,并为函数注明返回类型为()
元组,但是我们在写无返回值函数时不需要也不建议写明返回()
和返回类型为()
元组。