「 Rust 」数据类型

「 Rust 」数据类型


前言

Rust 是静态编译语言,在编译时必须知道所有变量的类型

  • 基于使用的值,编译器通常能够推断出它的具体类型
  • 但如果可能的类型比较多,例如把 String 转为整数的 parse 方法,就必须添加类型的标注,否则编译会报错
1
2
3
4
fn main() {
let guess: u32 = "42".parse().expect("Not a number");
println!("{}", guess);
}

由于针对数字 42 在 Rust 中有很多数据类型可以将其包含在内,如 i32u32 等等,所以要给变量具体指明类型,如果未指明,则会编译报错:

1
2
3
4
5
6
$ cargo run
error[E0282]: type annotations needed
--> src/main.rs:2:9
|
2 | let guess = "42".parse().expect("Not a number");
| ^^^^^ consider giving `guess` a type

标量类型

一个标量类型代表一个单个的值。

Rust 有四个主要的标量类型:

  • 整数类型
  • 浮点类型
  • 布尔类型
  • 字符类型

整数类型

整数类型没有小数部分,无符号整数类型以 u(usize)开头,有符号整数类型以 i (integer)开头,例如 u32 就是一个无符号的整数类型,占据 32 位的空间。

整数类型表

  • 每种都分 i 和 u,以及固定的位数
  • 有符号的范围是 -(2^n^ - 1) 到 2^n-1^ - 1
  • 无符号范围:0 到 2^n^ -1
Length Signed Unsigned
8-bit i8 u8
16-bit i16 u16
32-bit i32 u32
64-bit i64 u64
128-bit i128 u128
arch isize usize

isizeusize 类型的位数由程序运行的计算机的架构所决定,如果是 64 位计算机,那就是 64 位的。使用场景比如,对某个集合进行索引操作。

整数字面值

Number literals Example
Decimal 98_222
Hex 0xff
Octal 0o77
Binary 0b1111_0000
Byte (u8 only) b’A’
  • 除了 byte 类型外,所有的数字字面值都允许使用类型后缀,例如 57u8

    1
    2
    3
    4
    5
    fn main() {
    // 此时,foo 的类型为 u8,值为 57
    let foo = 57u8;
    println!("{}", foo)
    }
  • 整数的默认类型就是 i32

整数溢出

例如,u8 的范围是 0 - 255,如果把一个 u8 类型变量的值设为 256,那么:

  • 调试模式下编译: Rust 会检查整数溢出,如果发生溢出,程序在运行时就会 panic
  • 发布模式下编译: Rust 不会检查可能导致 panic 的整数溢出,如果发生溢出,Rust 会执行环绕操作,也就是 256 变为 1,257 变为 2,以此类推
1
2
3
4
5
fn main() {
let mut foo: u8 = 255;
foo = foo + 2;
println!("{}", foo)
}

cargo run

1
2
3
$ cargo run
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:3:11
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

cargo build –release

1
2
$ ./target/release/hello-world
1

浮点类型

Rust 的浮点类型使用了 IEEE-754 标准来表述,有两种基础的浮点类型,也就是含有小数部分的类型:

  • f32,单精度
  • f64,双精度,Rust 中默认的浮点类型

布尔类型

Rust 的布尔类型(true & false)占用 1 字节大小,符号为 bool。

字符类型

Rust 中 char 类型用来描述语言中最基础的单个字符,字符类型的字面值使用单引号,占用 4 字节的大小,是 Unicode 的标量值,可以表示比 ASCII 多得多的字符内容,如拼音,中日韩文,零长度空白字符,emoji 表情等。

范围是 U+0000 到 U+D7FFU+E000U+10FFFF

1
2
3
4
5
fn main() {
let x = 'z';
let y: char = '字';
let z = '😄';
}

复合类型

复合类型可以将多个值放到一个类型里。

Rust 有两个主要的复合类型:

  • 元组
  • 数组

元组(Tuple)

元组可以将多个类型的多个值放到一个类型里,并且长度固定,一旦声明不可改变。

元组的类型为:(类型1,类型2,...)

访问元组中的元素值可以使用模式匹配(destructure)和点标记法

模式匹配

1
2
3
4
5
6
fn main() {
let tup: (i32, bool, char) = (500, false, 'A');
// x, y, z 的类型与值分别对应 tup 中的三个元素,即 let x: 132 = 500
let (x, y, z) = tup;
println!("{}, {}, {}", x, y, z);
}

点标记法

1
2
3
4
fn main() {
let tup: (i32, bool, char) = (500, false, 'A');
println!("{}, {}, {}", tup.0, tup.1, tup.2)
}

点标记法的“索引“不可以是变量。

1
2
3
4
5
fn main() {
let tup: (i32, bool, char) = (500, false, 'A');
let i = 0;
println!("{}", tup.i)
}
1
2
3
4
5
6
$ cargo run
error[E0609]: no field `i` on type `(i32, bool, char)`
--> src/main.rs:4:24
|
4 | println!("{}", tup.i)
| ^

和 Python 等其他语言一样,当元组中只有一个元素,类型和值均需要加逗号,当没有逗号时,编译器会认为其是一个标量类型,括号会被视为多余。

1
2
3
4
fn main() {
let tup: (i32,) = (1,);
println!("{}", tup.0)
}

数组

数组可以将多个值放到一个类型中,但是数组中每个元素的类型必须相同,并且长度固定,一旦声明不可改变。

数组的类型为:[类型;长度]

1
2
3
fn main() {
let a: [i32;5] = [1, 2, 3, 4, 5];
}

除了上述的声明方式外,如果数组中的每个元素值都相同,那么可以快速声明为:

1
2
3
4
fn main() {
// 相当于 let a = [3, 3, 3, 3, 3]
let a = [3;5];
}

类似于 Golang,访问数组中的元素值可以使用索引法

索引越界

如果访问的索引超出了数组的范围,处理方式和 Golang 类似,也就是:

  • 编译时会通过,但是不是绝对的,Rust 编译器无法直接判断出是否越界等较复杂的情况
  • 运行时会报错,区别于 C 和 C++ 等,虽然数组在内存中为连续的地址,但是越界的内存空间不属于该数组,所以无法访问
Author

Shen Xianghong

Posted on

2022-01-16

Updated on

2023-06-19

Licensed under