Complex objects¶
本节介绍Rust中常用的复杂类型
容器类型¶
Vec¶
Rust中的向量类型是长度可变的数组,可以通过Vec::new()创建一个空的向量,也可以通过vec![]宏创建一个带有初始值的向量。Vec是泛型,使用Vec<type>可以指定向量中元素的类型。一个向量中存储元素的类型全部相同。向量及其所有元素是一个整体,对其中元素的可变引用也会导致向量本身无法再进行引用。
let v1 = Vec::new(); // Empty vector
let v2: Vec<i32> = Vec::new(); // Empty vector with type annotation
let v3 = vec![1, 2, 3]; // Vector with initial values
let v4 = vec![0; 10]; // Repeat values using colon
let v5 = vec![1i32, 2, 3]; // Vector with type specified to the first element
let v6 = Vec::with_capacity(10); // Vector with pre-allocated capacity 10
用let mut声明的向量可以被修改。Vec类型有如下方法:
push():向向量的尾部添加一个元素pop():从向量的尾部弹出一个元素extend():向向量的尾部添加另一个向量的所有元素reserve():调整向量的容量len():返回向量的长度capacity():返回向量的容量(当超出容量时,会重新分配内存空间)get():返回向量中指定位置的元素的引用,如果索引越界则返回Noneshrink_to_fit():将向量的容量调整为向量的长度,释放多余的内存空间sort()、sort_unstable():对向量中的元素进行排序sort_by()、sort_unstable_by():对向量中的元素进行排序,使用自定义的比较函数[]运算符:返回向量中指定位置的元素(右端项),或者修改向量中指定位置的元素(左端项)。
向量及其引用都可以用在for循环中。
结构体¶
有三种方法在Rust中声明结构体:
- C语言风格
- 命名元组
- unit struct
struct CStyle {
x: i32,
y: i32,
} // C-style struct with two fields
struct NamedTuple(i32, i32); // Named tuple struct with two fields
struct TypePattern(i32); // Type pattern struct with one field, can be used in `match` statement
struct UnitStruct; // Unit struct without fields
用pub关键字标记结构体后,其中的字段不能被外界访问。
枚举类型¶
Rust中的枚举类型关键字为enum,可以包含不同类型的值,每个值都可以有不同的类型。枚举类型的值可以通过match语句进行匹配。
enum Week {
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday,
} // Enum type with 7 variants
Rust中的枚举元素可以是不同的pattern,可以是结构体,也可以是元组,也可以是unit struct。
enum IpAddr {
V4(u8, u8, u8, u8), // Tuple
V6{ addr: String }, // Struct
LocalHost, // Unit
}
这些pattern可以用于match语句中进行模式匹配:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
用pub关键字标记枚举类型后,其中的各个字段都可以被外界访问。
泛型¶
泛型是一种类型模板,当一个函数/枚举/结构体需要处理多种类型的参数时,可以使用泛型。泛型的类型参数可以在函数名后面用尖括号指定,之后参数列表中用冒号指定泛型类型。
1 2 3 4 5 6 7 8 9 10 11 | |
Rust标准库中定义了一些常用的泛型类型:
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E),
}
Option<T>表示一个可能存在的值,可以是Some(T),也可以是None。类似于Python中的Optional。Result<T, E>表示一个可能出错的返回值,如果成功则是Ok(T),如果失败则是Err(E)。- 可以用于
match语句中进行模式匹配。
Impl & Trait¶
在Rust中,impl关键字用于为结构体实现方法,trait关键字用于声明方法,与结构体组合后相当于C++中的类。impl可以脱离trait单独使用。
- 当没有
trait时,impl的语法为impl <struct name> { <methods>},其中<struct name>可以是结构体名,也可以是泛型参数。 - 当有
trait时,impl的语法为impl <trait name> for <struct name> { <methods>},其中<trait name>是对应的trait名,<struct name>是需要实现该trait的结构体名。 - 特殊对象
self表示当前结构体的实例,可以以引用(&self、&mut self)或自身形态(self)出现。如果不使用self,则为静态方法/类方法,类似于Python中的@staticmethod或@classmethod。此时需要用<struct>::<method>调用,而不是<object>.<method>。 - 特殊类型
Self表示当前结构体的类型,可以用于返回值类型的声明。
孤儿规则
如果需要对类型A实现特征T,则T或A其中之一必须在当前作用域内定义。
struct Player {
first_name: String,
last_name: String,
}
trait GetFullName {
fn full_name(&self) -> String;
}
impl GetFullName for Player {
fn full_name(&self) -> String {
format!("{} {}", self.first_name, self.last_name)
}
}
fn main() {
let player_1 = Player {
first_name: "Rafael".to_string(),
last_name: "Nadal".to_string(),
};
println!("Player 01: {}", player_1.full_name());
}
trait可以和泛型结合使用,impl也可以对内置的类型使用,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
trait可以实现继承和多继承。
trait Person {
fn full_name(&self) -> String;
}
trait Employee : Person { // Employee inherits from Person
fn job_title(&self) -> String;
}
trait ExpatEmployee : Employee + Expat { // ExpatEmployee inherits from Employee and Expat
fn additional_tax(&self) -> f64;
}
在trait的声明中可以用函数体,此时表示方法的默认实现,在impl中可以重载这个方法。
struct Blog {};
struct Tweet {};
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}
impl Summary for Blog {} // Use default implementation
impl Summary for Tweet {
fn summarize(&self) -> String { // Override default implementation
String::from("Tweet")
}
}
特征约束¶
特征可以用于约束函数或泛型的类型。如
- 函数参数的限定,如
fn foo(bar: impl Copy)表示bar参数必须实现名为Copy的trait。 - 泛型参数的限定,如
<T: Copy>表示T必须实现名为Copy的trait,可以实现更复杂的类型控制。
使用+连接多个特征,表示参数必须同时实现多个特征。
fn foo<T: Copy + Display>(bar: T) {
println!("{}", bar); // `bar` must implement `Display` and `Copy`
}
使用where关键字可以使特征约束更加清晰。
fn foo<T, U>(bar: T, baz: U) -> U
where T: Copy + Display,
U: Add<Output = U>
{
println!("{}", bar); // `bar` must implement `Display` and `Copy`
baz + 1 // `baz` must implement `Add`
}
特征约束还可以用在impl语句中用于选择性地实现特征
trait Increment {
fn increment(&self) -> Self;
}
impl<T> Increment for T
where T: Add<Output = T> + Copy
{
fn increment(&self) -> Self {
*self + 1
}
}
表示Increment特征只能用于同时实现了Add<Output = T>和Copy的类型。
特征约束还可以用于函数返回值的限定,只能有一个特征,表示函数返回一个实现了特定特征的类型。但函数体内部不能返回多个不同的类型。
fn foo(x: bool) -> impl Display {
if x {
1
} else {
"hello" // Error, since 1 and "hello" have different types
}
}