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()
:返回向量中指定位置的元素的引用,如果索引越界则返回None
shrink_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
}
}