头图

本文介绍Rust提供的内置数据类型。

布尔类型

布尔类型代表“是”和“否”的逻辑值。它有两个值:truefalse,一般用在逻辑表达式中,可以执行“与”、“或”、“非”等运算:

fn main() {
    let x = true;
    let y: bool = !x;    //false,     取反运算
    
    let z = x && y;        //false,逻辑与运算,带有短路功能
    let z = x || y;        //true,逻辑或运算,带有短路功能
    let z = x & y;        //false,按位与运算,不带短路功能
    let z = x | y;        //true,按位或运算,不带短路功能
    let z = x ^ y;        //true,按位异或运算,不带短路功能
}

一些比较运算表达式的类型是布尔类型:

fn logical_op(x: i32, y: i32) {
    let z = x < y;    // z是布尔类型
    println!("z = {}", z);
}

布尔类型表达式可以用在if/while表达式中,作为条件表达式:

if a >= b {
    ...
} else {
    ...
}

C/C++会隐式地将字符、整数、浮点数和指针转换为布尔值;Python允许在布尔值上下文中使用字符串、列表、字典、集合。对于它们来说,如果这些值不为0或空,就将它们视为true。相对而言,Rust更加严格:if/while表达式的条件必须是布尔类型表达式。

Rust的as操作符可以把bool值显式地转换为整数类型:

assert_eq!(false as i32, 0);
assert_eq!(true as i32, 1);

但是,不能将数值类型转换为布尔类型,这种情况下必须进行显式转换:

let x = 0;
let y = 1;
assert_eq!(x as bool, false);    //错误,不允许使用as将数值类型转换为布尔类型
assert_eq!(y as bool, true);    //错误,不允许使用as将数值类型转换为布尔类型

//必须使用显式比较
if x == 0 {...}
if y == 1 {...}

理论上来讲,一个布尔类型仅仅需要一个比特位即可表示,但是Rust在内存中使用整整一个字节作为bool值,因此允许创建一个指向布尔值的指针。

字符类型

Rust的字符类型char使用4个字节的内存空间来保存单个unicode码点。在代码中,单个字符字面量使用一对单引号包围:

let love = '❤' //可以直接嵌入任何Unicode字符

字符字面量中可以使用转义符:

let c1 = '\n';          //换行符
let c2 = '\u{7FFF}'; //Unicode字符

根据Unicode字符的标准,char类型保存的码点范围在[0x0000,0xD7FF]U[0xE000,0x10FFFF]之中。Rust的类型系统会进行动态检查,保证char的值始终在上述合法范围当中。

Rust从来不会隐式地在char与其他类型之间进行转换。如果有需要,可以使用as操作符将char类型转换为数值类型。如果目标数值类型的大小不足4个字节,字符的高位内存会被截断:

assert_eq!('*' as i32, 42);
assert_eq!('😻' as i32, 128571);
assert_eq!('😻' as i8, 59);    //高位字节被截断

出乎意料的是,as操作符只能将u8类型转换为char类型,这是因为任何其他数值类型都有可能会产生非法的unicode码点,这样就会导致运行时的字符值检查。不过,标准库的函数std::char::from_u32()可以将u32值转换为Option<char>值,从而确保了正确性。

除此之外, 标准库也为字符类型提供了常用的工具函数,例如:

assert_eq!('*'.is_alphabetic(), false);
assert_eq!('b'.is_alphabetic(), true);
assert_eq!('8'.to_digit(), Some(8));
assert_eq!(std::char::from_digit(2, 10), Some('2'));

整数类型

与其它大多数编程语言的整数类型相同,Rust整数类型是一组固定大小的类型,与现代CPU硬件里直接实现的类型对应。Rust数值类型的命名遵循一种规律,即:同时写出位宽以及表现形式:

字节数无符号整数有符号整数
1u8i8
2u16i16
4u32i32
8u64i64
机器相关usizeisize

Rust的整数类型使用补码表示。如果一个变量是有符号类型,那么它的最高位就是符号位, 用来区分这个值是正数还是负数;如果一个变量是无符号类型,那么它的最高位和其它位一样,都用来表示数据。

需要特别注意的是usizeisize类型,它们占据的内存空间是不确定的,与程序所执行的平台相关。如果在32位系统上,就使用四个字节;如果在64位系统上,就使用八个字节。

整数值的字面量可以有许多的表示方式:

let var1: i32 = 32;        //十进制
let var2: i32 = 0xFF;    //十六进制
let var3: i32 = 0o55;    //八进制
let var4: i32 = 0b1001;    //二进制

字面量可以在任意的地方添加下划线,从而提高可读性。例如:

let var5 = 0x_1234_ABCD;    

此外,如果不指定整数字面量的类型,则默认使用i32类型;如果希望显式地制定类型,可以在字面量的末尾添加后缀:

let var8 = 32;            //不写类型,默认是i32
let var6 = 123usize;    //i6是usize类型
let var7 = 0x_ff_u8;    //var7是u8类型

u8值有点特殊,因为它可以表示非常常用的ASCII字符,Rust为此专门提供了字节字面量

let a = b'A';    //u8,表示字符A,也就是65

字节字面量同样可以使用转义符:

let b = b'\\';    //u8,表示字符\,也就是92
let c = b'\n';    //u8,表示换行符,也就是10
let d = b'\t';  //u8,表示制表符,也就是9

不同的整数类型之间可以使用as操作符进行类型转换。例如:

assert_eq!(10_i8 as u16, 10_u16);    //对于正数,由小到大时在高位填充0,正确。
assert_eq!(10_i8 as u16, 10_u16);    //对于正数,由小到大时在高位填充0,正确。
assert_eq!(-1i16 as i32, -1_i32);    //对于负数,由小到大时在高位填充1,正确。

assert_eq!(1000_i16 as u8, 232_u8);        //发生类型截断,高位数据被丢弃。
assert_eq!(65535_u32 as i16, -1_i16);    //发生类型截断,高位数据被丢弃。

Rust整数类型运算的方式与其他语言有所不同。在整数的算术运算中,有一个比较头疼的事情是“溢出”。在C语言中,无符号类型的算术运算永远不会发生溢出,如果超出了表示范围,则自动舍弃高位数据;对于有符号类型的算术运算,溢出后的行为是未定义的。

未定义行为有利于编译器做出一些激进的性能优化,但是有可能在极端情况下产生诡异的BUG。Rust的设计思路更倾向于预防BUG,而不是无条件地压榨效率。因此,Rust在这个问题上的处理方式为:默认情况下,debug模式编译时编译器会添加额外的代码来检查溢出,一旦发生了溢出,就会引发panic;在release模式编译时,编译器不会检查整数溢出,而是采用舍弃高位的方式。与此同时,编译器还提供了一个独立的编译选项,用来手动设置溢出时的处理策略:overflow-checks=yes/no

如果需要更精细地自主控制整数溢出的行为,可以调用标准库中的check_*saturating_*wrapping_*系列函数,其中:

  • checked_*系列函数返回的类型是Option<_>,当出现溢出时,返回的值是None
  • saturating_*系列函数返回的类型是整数,当出现溢出时,返回的值是该类型可表示范围的最大或最小值。
  • wrapping_*系列函数直接进行截断。

当安全性非常重要时,应该尽量使用这几个方法来代替默认的算术运算符:

fn main() {
    let i = 100i8;
    println!("checked: {:?}", i.checked_add(i));        //None
    println!("saturating {:?}", i.saturating_add(i));    //127
    println!("wrapping: {:?}", i.wrapping_add(i));        //-56
}

浮点类型

Rust支持IEEE规定的单、双精度浮点类型,遵循IEEE 754-2008标准。这些类型包含正、负无穷,区分正、负零,还有一个非数值的值:

类型精度范围
f32IEEE单精度(至少6位小数)$$(-3.4\times10^{38},3.4\times10^{38})$$
f64IEEE双精度(至少15位小数)$$(-1.8\times10^{308},1.8\times10^{308})$$

与整数类型相同,可以使用后缀f32f64来显式地制定字面量的类型。

Rust标准库为IEEE定义的浮点数特殊值定义了常量,比如INFINITYNEG_INFINITYNAN,以及MINMAX等。同时,标准库中也为两种精度的浮点数类型都定义了常用的数学函数:

fn main() {
    assert_eq!(5f32.sqrt() * 5f32.sqrt(), 5.0);
    assert_eq!(-1.01f64.floor(), -1.0);
    assert!((-1.0 / std::f32::INFINITY).is_sign_negative());
}

此外,Rust不接受整数类型到浮点类型的自动类型转换。


您的好友
4 声望0 粉丝