Rust 学习 07

archive time: 2022-05-20

努力学习 Rust

Struct 方法

struct 方法类似于 Java 里的类中的成员方法和类方法

也就是

  • 方法 是在 struct (enum, trait) 的 上下文 中定义的
  • 方法的第一个参数是 self (类似 Python), 表示调用的实例

对应概念叫做 关联函数, 与实例无关, 即不需要使用 self

#[derive(Debug)]
struct Rectangle {
    width: u16,
    length: u16,
}
// 表示实现结构体相关内容
impl Rectangle {
    // 关联函数
    pub fn new(width: u16, length: u16) -> Self {
        Rectangle { width, length }
    }
    // 结构体方法
    pub fn area(&self) -> u16 {
        self.length * self.width
    }
}

fn main() {
    // 使用关联函数
    let rec = Rectangle::new(10, 16);
    // 使用结构体方法
    println!("{}", rec.area());
}

枚举 (enum)

Rust 里的枚举比 C 或 Java 的枚举要高级很多, 而且还可以使用模式匹配, 是一个很好用的工具

例如:

fn main() {
    let ipv4 = IPAddress::IPv4(192, 168, 1, 1);
    let ipv6 = IPAddress::IPv6("2001:DB8:2de::e13".to_string());
    route(ipv4);
    route(ipv6);
}

#[derive(Debug)]
enum IPAddress {
    IPv4(u8, u8, u8, u8),
    IPv6(String),
}
// 使用枚举类型的函数
fn route(ip_addr: IPAddress) {
    println!("{:?}", ip_addr);
}

这里, IPv4IPv6 都是 IPAdress枚举变体

IPv4 后面所带的就是每个变体所附带的数据

枚举方法

类似结构体, 枚举也可以定义方法, 即使用 impl 关键字

fn main() {
    let ipv4 = IPAddress::IPv4(192, 168, 1, 1);
    let ipv6 = IPAddress::IPv6("2001:DB8:2de::e13".to_string());
    ipv4.route();
    ipv6.route();
}

#[derive(Debug)]
enum IPAddress {
    IPv4(u8, u8, u8, u8),
    IPv6(String),
}

impl IPAddress {
    fn route(&self) {
        println!("{:?}", self);
    }
}

Option 枚举

Option 和 Haskell 的 Maybe 类型, 有 Some (Just) 和 None (Nothing) 两个变体

例子如下

fn main() {
    let some_num: Option<u8> = Some(10);
    let nothing: Option<u8> = None;
    println!("{}, {}", get_val(some_num), get_val(nothing));
}

fn get_val(opt: Option<u8>) -> i8 {
    match opt {
        Some(n) => match n.try_into() {
            Ok(v) => v,
            Err(_) => -1,
        },
        None => -1,
    }
}

下面的 match 就使用了 模式匹配

对于 Option 类型, 我们还可以这样

if let Some(n) = some_num {
    println!("{}", n + 10);
}

match

match 是分支语句, 每个 arm (分支) 对应着一个 模式, 例如

match opt {
    Some(n) => match n.try_into() {
        Ok(v) => v,
        Err(_) => -1,
    },
    None => -1,
}

里的 Some(n)None 就是两个模式, 模式可以包含字面值, 变量名 以及 通配符(_) 等

这里的 n 就是变量名, 如果 opt 是 Some(_) 这种形式的, 那么就把 Some 里的值赋给 n 来使用

None 就是字面值

但是 match 必须要覆盖所有可能, 即匹配 Option 时, 需要处理 Some 和 None 两种形式

Rust 的 match 还可以这样

fn main() {
    let x = Some(8u8);
    let res = match x {
        Some(v) if v < 10 => v + 10,
        Some(v) => v,
        None => 0,
    };
    println!("{}", res);
}

即, 使用 if 进行细化筛选

Rust 代码组织

Rust 的代码组织大概可分为

  • 包 (package)
  • 单元包 (crate)
  • 模块 (module)

crate

crate 可以分成 librarybinary

  1. Crate Root
    • 是源代码文件
    • 编译器从这里开始组成 crate 的 root module
  2. Package
    • 只能包含 1 个 Cargo.toml, 描述了如何构建
    • 只能包含一个或零个 library crate
    • 可以有任意多个 binary crate
    • 最少要有一个 crate

Module

Module 多用来控制作用域和私有性

module 用于在一个 crate 里对代码进行分组, 增加易读性和可复用性

module 还可控制项目 (item) 的私有性 (pub), 默认是私有的

建立 module 可以使用 mod 关键字, 而且 mod 之间可以嵌套

- src
  - utils
    - tools.rs
    - mod.rs
  - lib.rs
  - main.rs

各个文件内的内容如下

/// utils/tools.rs
// 可以定义一些函数和结构体
pub fn mul<T>(a: T, b: T) -> T
where
    T: std::ops::Mul<Output = T>,
{
    a * b
}

/// utils/mod.rs
// utils 模块的控制文件
pub mod tools;

/// lib.rs
// library crate 的 crate root
mod utils; // 声明 utils mod
// 定义一些函数
pub fn add<T>(a: T, b: T) -> T
where
    T: std::ops::Add<Output = T>,
{
    a + b
}

/// main.rs
// binary crate 的 crate root
mod utils; // 声明 utils mod
// 使用 mod 函数
use crate::utils::tools::mul;
// 使用项目名调用 library crate 中的内容
use learn_rust::add;
// 主函数
fn main() {
    let x = Some(8u8);
    let res = match x {
        Some(v) if v < 10 => v + 10,
        Some(v) => v,
        None => 0,
    };
    println!("{}", res);
    // 一些调用示例
    println!("{}", add(10, 20));
    println!("{}", mul(10, 20));
}

路径

路径可以分为绝对路径和相对路径

绝对路径从 crate 开始

而相对路径从当前模块开始来开始找

对于相对路径, 如果要访问上一级路径, 可以使用 super 关键字

一般推荐绝对路径

私有性

默认的模块和项目都是私有的

如果想要对外开放, 那么可以使用 pub 关键字来开放

对于结构体如果用 pub 修饰了, 那么结构体类型是开放的

但是里面的字段如果没有 pub 修饰, 那么默认还是私有的

// 可以使用 Matrix 类型
pub struct Matrix<T>
where
    T: std::ops::Add<Output = T>
        + std::ops::Mul<Output = T>
        + std::ops::Sub<Output = T>
        + Eq
        + PartialEq
        + Ord
        + PartialOrd,
{
    // 外部无法访问字段
    data: Vec<T>,
    pub size: (u16, u16), // 公开
}

但是对于 enum 如果公开了, 那么每个 variant 都是公开的

可以使用 pub use 来重新导出


好, 今天就学到这罢, 摸!