rust结构体、枚举、包和作用域


学习视频:B站

struct 结构体

一旦struct是可变的,里面的字段都是可变的。

demo:

struct User {
    username: String,
    email: String,
    active: bool,
}
fn main() {
    let user = User {
        username: String::from("lei"),
        email: String::from("123@gmail.com"),
        active: false,
    };
}

struct更新语法

let user2 = User {
    active: true,
    ..user // 表示剩下的都和user有相同的字段
};

Tuple struct

image-20221118142351030

例子

不使用struct:

fn main() {
    let w = 3;
    let l = 5;
    println!("{}",area(w, l));
}

fn area(width: u32, length: u32) -> u32 {
    width * length
}

使用Tuple:

fn main() {
    let rect = (3, 5);
    println!("{}",area(rect));
}

fn area(dim: (u32, u32)) -> u32 {
    dim.0 * dim.1
}

使用struct:

把长和宽联系在了一起,比较直观。

image-20221118143415741

struct方法

与函数不同之处:

  • 是在struct的上下文中定义
  • 第一个参数是self,也就是方法被调用的struct实例

demo:

struct Rectangle {
    width: u32,
    length: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.length
    }
    
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.length > other.length
    }
}

impl里面也可以定义关联函数,不是方法,不以self作为第一个参数。

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle { width: size, length: size }
    }
}

// 调用:
let s = Rectangle::square(20);

关联函数通常用于构造器。

impl块可以有多个。

枚举

定义枚举

image-20221118153703445

可以把数据附加到枚举的变体中:

  • 不需要额外使用struct
  • 每个变体拥有不同的类型及关联的数据量
enum IpAddrKind {
    V4(u8,u8,u8,u8),
    V6(String),
}

fn main() {
    let four = IpAddrKind::V4(127,0,0,1);
    let six = IpAddrKind::V6(String::from("::1"));
}

方法

也用impl,类似struct中的方法。

Option枚举:

enum Option<T> {
    /// No value.
    None,
    /// Some value of type `T`.
    Some(T),
}

image-20221118154911348

Option<T>比Null好在哪?

要想使用Option<T>中的T,必须将它转化为T,避免了假设T不为null的问题。

match

match匹配必须匹配所有的模式,如果剩下的不想管了,用下划线通配符"_"

可以直接返回,也可花括号多行然后返回,也可匹配值。

enum IpAddrKind {
    V4,
    V6(String),
}

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6(String::from("haha"));

    println!("{}",ip_type(six));

    // haha
    // 6
}

fn ip_type(ip_kind: IpAddrKind) -> i32 {
    match ip_kind {
        IpAddrKind::V4 => 4,
        IpAddrKind::V6(str) => {
            println!("{}",str);
            6
        },
    }
}

match匹配Option<T>


fn main() {
    let five = Some(5);
    let six = plus_one(five);
    let none = plus_one(None);

    print_option(five);
    print_option(six);
    print_option(none);

    // value: 5
    // value: 6
    // No value
}


fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i+1),
    }
}

fn print_option(x: Option<i32>) {
    match x {
        None => {
            println!("No value");
        }
        Some(i) => {
            println!("value: {}",i);
        }
    }
}

if let

只关心一种匹配,忽略其他匹配。可以和else一起。

let v = Some(2u8);
match v {
    Some(0) => println!("three"),
    _ => (),
}

if let Some(0) = v {
    println!("three");
} else {
    println!("others");
}

Package, Crate, Module

image-20221118162220154

看着有点蒙:

image-20221118162631425

image-20221118164453766

crate的作用

  • 将相关功能组合到一个作用域中,以便在项目间进行共享,防止冲突。
  • 例如rand crate,访问它的功能需要通过它的名字:rand

module:

  • 在一个crate内,将代码进行分组
  • 增加可读性、复用性
  • 控制项目私有性,private,public

demo:

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}

路径

  • 绝对路径,从crate root开始
  • 相对路径,从当前模块开始

Rust所有条目默认是私有的。父模块无法访问子模块的私有条目,子模块可以使用所有祖先模块的条目。

pub:标记公共的

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant() {
    // 绝对路径
    crate::front_of_house::hosting::add_to_waitlist();
    // 相对路径
    front_of_house::hosting::add_to_waitlist();
}

super访问父级模块中的内容。

fn haha() {}

mod front_of_house {
   pub fn add_to_waitlist() {
        super::haha();
        crate::haha();
    }
}

pub放在struct前,struct里面的字段还是默认私有。

pub放在enum前,enum里面的字段默认共有。(enum本来就是给大家共用的)

use

将路径引入作用域。

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {

    crate::front_of_house::hosting::add_to_waitlist();

    hosting::add_to_waitlist();
}

use引入也需要遵守私有性规则。use也可以指定相对路径。

use的习惯用法

  • 函数,指定到父级
  • struct,enum,指定到本身
  • 不同模块同名条目,指定到父级。
use std::fmt::Result;
use std::io::Result as IoResult;

use引入后在作用域是私有的,外部代码看不到。

use crate::front_of_house::hosting;

想要外部代码也看到:

pub use crate::front_of_house::hosting;

使用外部包

  • cargo.toml添加依赖包(package)

  • use引入作用域

cargo build blocking wait for file lock on package cache问题:

image-20221118180036708

如之前的rand例子:生成一个随机数

使用嵌套路径清理大量use条目:

use std::cmp::Ordering;
use std::io;
// 可替换为
use std::{cmp::Ordering, io};


use std::io;
use std::io::Write;
// 可替换为
use std::io::{self, Write};

也可使用通配符,但是通常不用。

将模块拆分为不同的文件

模块定义时,如果模块名后面是分号,而不是代码块:

  • Rust会从与模块同名的文件中加载内容。
  • 模块树的结构不会发生变化。

写在lib.rs下面:

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

拆分文件:

image-20221118181204299

//lib.rs
mod front_of_house;

// front_of_house.rs
pub mod hosting;

// front_of_house文件夹下hosting.rs
pub fn add_to_waitlist() {}