跳转至

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中声明结构体:

  1. C语言风格
  2. 命名元组
  3. 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语句中进行模式匹配:

Download source code

 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
enum IpAddr {
    V4(u8, u8, u8, u8), // Tuple
    V6{ addr: String }, // Struct
    LocalHost, // Unit
}

fn main() {
    let v4 = IpAddr::V4(4, 4, 4, 4);
    let v4_invalid = IpAddr::V4(0, 0, 0, 0);
    let v6 = IpAddr::V6{ addr: String::from("::1") };
    let local = IpAddr::LocalHost;
    print_ip(v4);
    print_ip(v4_invalid);
    print_ip(v6);
    print_ip(local);
}

fn print_ip(ip: IpAddr) {
    match ip {
        IpAddr::V4(a, b, c, d) if a != 0 || b != 0 || c != 0 || d != 0
            => println!("{}.{}.{}.{}", a, b, c, d),
        IpAddr::V4(a, b, c, d)
            => println!("{}.{}.{}.{} is not an valid IPv4 address", a, b, c, d),
        IpAddr::V6{ addr } => println!("{}", addr),
        IpAddr::LocalHost => println!("localhost"),
    }
}

pub关键字标记枚举类型后,其中的各个字段都可以被外界访问。

泛型

泛型是一种类型模板,当一个函数/枚举/结构体需要处理多种类型的参数时,可以使用泛型。泛型的类型参数可以在函数名后面用尖括号指定,之后参数列表中用冒号指定泛型类型。

Download source code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fn max<T> (a: T, b: T) -> T
    where T: std::cmp::PartialOrd
{
    if a > b { a } else { b }
}

fn main() {
    println!("{}", max(1, 2)); // i32
    println!("{}", max(1.0, 2.0)); // f64
    println!("{}", max("a", "b")); // &str
}

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单独使用。

  1. 当没有trait时,impl的语法为impl <struct name> { <methods>},其中<struct name>可以是结构体名,也可以是泛型参数。
  2. 当有trait时,impl的语法为impl <trait name> for <struct name> { <methods>},其中<trait name>是对应的trait名,<struct name>是需要实现该trait的结构体名。
  3. 特殊对象self表示当前结构体的实例,可以以引用(&self&mut self)或自身形态(self)出现。如果不使用self,则为静态方法/类方法,类似于Python中的@staticmethod@classmethod。此时需要用<struct>::<method>调用,而不是<object>.<method>
  4. 特殊类型Self表示当前结构体的类型,可以用于返回值类型的声明。

孤儿规则

如果需要对类型A实现特征T,则TA其中之一必须在当前作用域内定义。

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也可以对内置的类型使用,如:

Download source code

 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 From<T> {
    fn from_u8(x: T) -> Self;
}
impl From<u8> for u8 {
    fn from_u8(x: u8) -> Self {
        x
    }
}
impl From<u8> for u16 {
    fn from_u8(x: u8) -> Self {
        x as u16
    }
}
impl From<u8> for i8 {
    fn from_u8(x: u8) -> Self {
        x as i8
    }
}
impl From<u8> for i16 {
    fn from_u8(x: u8) -> Self {
        x as i16
    }
}

fn main() {
    println!("{}", i16::from_u8(1 as u8));
}

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")
    }
}

特征约束

特征可以用于约束函数或泛型的类型。如

  1. 函数参数的限定,如fn foo(bar: impl Copy)表示bar参数必须实现名为Copytrait
  2. 泛型参数的限定,如<T: Copy>表示T必须实现名为Copytrait,可以实现更复杂的类型控制。

使用+连接多个特征,表示参数必须同时实现多个特征。

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
    }
}

评论