Rust核心数据结构 #
文本类型(Textual types) #
char
和str
类型用于保存文本数据。
char
类型的值是一个Unicode标量值,用32位无符号整数表示,范围在0x0000到0xD7FF或0xE000到0x10FFFF之间。创建超出此范围的char
会导致未定义行为。[char]
实际上是长度为1的UCS-4/UTF-32字符串。
str
类型的值与[u8]
表示方式相同,是一个8位无符号字节的切片。不过,Rust标准库对str
有额外假设:操作str
的方法假定并确保其中的数据是有效的UTF-8编码。用非UTF-8缓冲区调用str
方法可能会导致当前或将来出现未定义行为。
由于str
是动态大小类型,只能通过指针类型(如&str
)来实例化。
str和String #
数组(array)和切片(slice) #
数组(array) #
数组是类型为T
的N
个元素的固定大小序列。数组类型写作[T; N]
。大小是计算结果为usize
的常量表达式。
示例:
1// A stack-allocated array
2let array: [i32; 3] = [1, 2, 3];
3
4// A heap-allocated array, coerced to a slice
5let boxed_array: Box<[i32]> = Box::new([1, 2, 3]);
数组的所有元素总是会被初始化,并且在安全的方法和操作中,对数组的访问总是会进行边界检查。
注意
标准库类型Vec<T>
提供了一种在堆上分配的可变大小的数组类型。
切片(slice) #
切片是一种动态大小的类型,表示对类型T
的元素序列的“视图”。切片类型写作[T]
。
切片类型通常通过指针类型使用。例如:
&[T]
:一个“共享切片(shared slice)”,通常简称为“切片”。它不拥有它所指向的数据;它借用这些数据。
&mut [T]
:一个“可变切片(mutable slice)”。它可变地借用它所指向的数据。
Box<[T]>
:一个“boxed切片(boxed slice’)”
示例:
1// A heap-allocated array, coerced to a slice
2let boxed_array: Box<[i32]> = Box::new([1, 2, 3]);
3
4// A (shared) slice into an array
5let slice: &[i32] = &boxed_array[..];
切片的所有元素总是已初始化的,并且在安全方法和操作中,对切片的访问总是会进行边界检查。
1let array: [u8; 5] = [1, 2, 3, 4, 5];
2let slice: &[u8] = &array;
3let (first_slice, second_slice) = slice.split_at(3);
4println!("{first_slice:?} {second_slice:?}"); // [1, 2, 3] [4, 5]
Vector(Vec<T>
)
#
Vec<T>
是Rust中在堆上分配内存的方法之一,另一种方法是智能指针如(Box)。
Rust中的一些类型就是包装了Vec,并在其上增加一些方法。例如String封装了Vec<u8>
:
1#[derive(PartialEq, PartialOrd, Eq, Ord)]
2#[stable(feature = "rust1", since = "1.0.0")]
3#[cfg_attr(not(test), lang = "String")]
4pub struct String {
5 vec: Vec<u8>,
6}
7...
HashMap #
- rust语言基础学习: rust中的HashMap
- Storing Keys with Associated Values in Hash Maps
- HashMap - Std library types - rust-by-example
HashMap可以与任意键和值一起使用,但键必须实现std::cmp::Eq
和std::hash::Hash
trait。许多trait (如 Eq 和 Hash)可以使用#[derive]
attribute自动派生。
元组(Tuple) #
元组类型是一类用于异构类型列表的结构化类型。
元组类型的语法是用括号括起来的、以逗号分隔的类型列表。一元元组在其元素类型后需要一个逗号,以便与带括号的类型区分开。
元组类型的字段数量等于类型列表的长度。这个字段数量决定了元组的"元数"。具有 n 个字段的元组称为"n 元元组"。例如,具有 2 个字段的元组是二元元组。
元组的字段使用与其在类型列表中的位置相对应的递增数字命名。第一个字段是 0,第二个字段是 1,依此类推。每个字段的类型与元组类型列表中相同位置的类型一致。
为了方便和历史原因,没有字段的元组类型()
通常被称为"unit"或"unit类型"。它的唯一值也被称为"unit"或"unit值"。
元组类型的一些示例:
()
(unit类型)(i32,)
(一元元组)(f64, f64)
(String, i32)
(i32, String)
(与前一个示例不同的类型)(i32, f64, Vec<String>, Option<bool>)
这种类型的值通过元组表达式构造。此外,如果某个表达式没有其他有意义的值可以求值,则会产生"unit值。元组字段可以通过元组索引表达式或模式匹配来访问。
结构体(struct) #
结构体类型是由其他类型组成的异质性产品(heterogeneous product),称为该类型的字段。
可以通过结构体表达式构造结构体的新实例。结构体表达式示例:
1Point {x: 10.0, y: 20.0};
2NothingInMe {};
3TuplePoint(10.0, 20.0);
4TuplePoint { 0: 10.0, 1: 20.0 }; // Results in the same value as the above line
5let u = game::User {name: "Joe", age: 35, score: 100_000};
6some_fn::<Cookie>(Cookie);
默认情况下,结构体的内存布局是未定义的,以允许编译器进行优化,如字段重排,但可以通过repr attribute来固定它。在任何情况下,字段可以以任意顺序在相应的结构体表达式中给出;生成的结构体值将始终具有相同的内存布局。
结构体的字段可以通过可见性修饰符进行限定,以允许在模块外访问结构体中的数据。
元组结构体类型与结构体类型类似,不同之处在于其字段是匿名的。
类似单元的结构体类型与结构体类型类似,只是它没有字段。通过关联的结构体表达式构造的唯一值就是该类型所拥有的唯一值。
结构体的最简单形式是空结构体:
1struct EmptyStruct {}
单元结构体(可以认为是另一种形式的空结构体):
1struct UnitStruct;
元组结构体,是一种特殊形式的结构体,其行为类似于元组。元组结构体与常规结构体的主要区别在于,在元组结构中,字段值没有名称,只有类型。
1struct TupleStruct(String, String);
普通结构体:
1struct TypicalStruct {
2 name: String,
3 value: String,
4 number: i32,
5}
枚举(enum) #
枚举类型是一种名义上异质的互斥联合类型(nominal, heterogeneous disjoint union type),由枚举项的名称表示。
枚举项同时声明了类型和多个变体,每个变体都有独立的名称,并且具有结构体、元组结构体或类似单元结构的语法。
可以通过结构体表达式构造新的枚举实例。
任何枚举值都会消耗与其对应枚举类型中最大变体相同的内存,以及存储判别值所需的内存。
枚举类型不能像类型那样通过结构化表示,必须通过枚举项的命名引用来表示。
Rust的枚举与C、C++、Java或C#等语言中的枚举截然不同。在这些语言中,枚举被有效地用作定义常量值的一种方式。Rust的枚举可以模拟枚举,但它们在概念上是不同的。
1enum Message {
2 Quit, // 没有关联任何数据
3 Move { x: i32, y: i32 }, // 带有命名字段的结构体
4 Write(String), // 带有 String 类型的元组
5 ChangeColor(i32, i32, i32), // 带有三个 i32 类型的元组
6}
类型别名(Type aliases) #
类型别名在其所在模块或块的类型命名空间中定义了一个现有类型的新名称。类型别名使用关键字 type
声明。每个值只有一个特定类型,但可以实现多个不同的traits,并可兼容多个类型约束。
例如,以下定义了类型 Point
作为类型 (u8, u8)
的同义词,(u8, u8)
是无符号8位整数对的类型:
1type Point = (u8, u8);
2let p: Point = (41, 68);
不能使用元组结构体或单元结构体的类型别名来限定该类型的构造函数:
1struct MyStruct(u32);
2
3use MyStruct as UseAlias;
4type TypeAlias = MyStruct;
5
6let _ = UseAlias(5); // OK
7let _ = TypeAlias(5); // Doesn't work
类型别名在不作为关联类型使用时,必须指定类型(Type),并且不能包含类型参数约束(TypeParamBounds)。
当类型别名作为trait中的关联类型使用时,不能包含类型(Type)说明,但可以包含类型参数约束(TypeParamBounds)。
当类型别名作为trait实现中的关联类型使用时,必须包含类型(Type)说明,并且不能包含类型参数约束(TypeParamBounds)。
在trait实现中的类型别名的等号前的where 子句(如 type TypeAlias<T> where T: Foo = Bar<T>
)已弃用。推荐使用等号后面的 where 子句(如 type TypeAlias<T> = Bar<T> where T: Foo
)。
定义类型别名不会创建新类型。类型别名有两种常见用途:
- 出于人体工程学和方便库用户的目的,为公共类型提供别名类型定义
- 提供与更复杂的类型组合相对应的简写类型