Administrator
发布于 2025-12-07 / 3 阅读
0

模式匹配

match

match就是一个匹配器,需要穷举出所有的情况,如下图

enum TrafficLight {
    Red,
    Yellow,
    Green,
}
fn main() {
    let light = TrafficLight::Yellow;
    match light {
        TrafficLight::Red | TrafficLight::Yellow => println!("不能通过"),
        TrafficLight::Green => println!("可以通过"),
    };
}

这里" | "就是或,直接看代码里的就是,红或黄都不能过

值得注意的是,他后面匹配条件后面必须要跟表达式=>表达式

假如说不关心剩下的内容,也可以用"_"代表为列举的所有可能

模式绑定

在定义枚举的时候可以在里面加入变量,就像下面这样

enum LoginStatus {
    LoginedOut,
    LoginedIn(String),
    logined(String),
}
fn user_loginstatus(x: LoginStatus) {
    match x {
        LoginStatus::LoginedOut => println!("未登录"),
        LoginStatus::LoginedIn(name) => println!("用户{}已登陆", name),
        LoginStatus::logined(reason) => println!("因为{},无法登录", reason),
    }
}
fn main() {
    let x = LoginStatus::LoginedIn(String::from("张三"));
    user_loginstatus(x);
}

然后在匹配LoginStatus::LoginedIn(String)时,把他内部储存的值绑定到了name上,也就是“张三”这个字符串被绑定到了name上,所以打印出来就是这个效果

if let匹配

这个方式用于匹配只有一个模式需要被处理的情况

enum LoginStatus {
    LoginedOut,
    LoginedIn(String),
    logined(String),
}

这上面三种情况,假如说只需要处理第二个

换做match就需要为了专门匹配这一个写一长串

    let x = LoginStatus::LoginedIn(String::from("张三"));
    match x {
        LoginStatus::LoginedIn(name) => println!("用户{}已登陆", name),
        _ => (),
    }

但是if let方式不需要

    let x = LoginStatus::LoginedIn(String::from("张三"));
    if let LoginStatus::LoginedIn(name) = x {
        println!("用户张三已登陆");
    }

其实感觉没省啥事。。。

模式使用场景

match适用于匹配多个模式,不想匹配的用_

if let适用于匹配单个模式,忽略剩下所有的模式

while let条件循环

我在看这部分的时候比较懵逼

// Vec是动态数组
let mut stack = Vec::new();

// 向数组尾部插入元素
stack.push(1);
stack.push(2);
stack.push(3);

// stack.pop从数组尾部弹出元素
while let Some(top) = stack.pop() {
    println!("{}", top);
}

现在来挨个解读

这先创建了一个动态数组

然后在里面依次插入1,2,3

然后使用我们的重头戏while let进行操作

这里是一个尾部弹出操作,如果这个stack里面还有元素,就会从后往前弹出

只要stack.pop( )得到的结果是个Some(top),就会把弹出的值绑定到top上,然后执行括号内的代码,一直这么循环;循环到第四次时,stack都pop完了,最后会弹出一个None,直接结束循环,不会执行括号内的内容

方法

啥是方法?

众所周知,函数可以定义在任何地方,谁都能调用

而方法不一样,他定义在某个类型(结构体、枚举、trait)的impl里

只有手上有这个类型的实例,才能调用为这个类型实现的方法

struct Rectangle {
    height: u32,
    width: u32,
}
impl Rectangle {
    //方法的第一个参数是&self
    fn area(&self) -> u32 {
        self.height * self.width
    }
}
fn main() {
    let rect1 = Rectangle {
        height: 15,
        width: 20,
    };
    println!("这个矩形的面积是{}", rect1.area());
}

方法的第一个参数是&self,self代表“当前这个对象”,这里的类型其实就是&Rectangle函数体里使用self.height和self.width访问字段

方法就是fn area(&self,其他参数...)然后在rect.area(...)调用时,编译器自己给&rect塞进self

self

这一块和所有权/借用也能搭上边,有三种:

self/&self/&mut self

依次是消费(拿所有权,用完后对象也没了)、只读、可变

struct Counter {
    value: i32,
}

impl Counter {
    // 只读:不可变借用
    fn get(&self) -> i32 {
        self.value
    }

    // 可改:可变借用
    fn inc(&mut self) {
        self.value += 1;
    }

    // 消费:拿走所有权
    fn into_value(self) -> i32 {
        self.value
    }
}

在使用的时候

fn main() {
    let mut c = Counter { value: 0 };

    c.inc();             // 需要 &mut self,所以 c 必须是 mut 的
    println!("{}", c.get());

    let v = c.into_value(); // 把 c 消费掉
    // 这里再用 c 就会报错:value borrowed after move
}

其实这个所有权就和函数参数的所有权一样,根据需不需要改变来决定调用哪个方法、选择哪种所有权,只不过把类型写成了Self

带有多个参数的方法

方法名可以和字段名一样

struct User {
    name: String,
}

impl User {
    // 字段叫 name
    // 方法也叫 name
    fn name(&self) -> &str {
        &self.name
    }
}

fn main() {
    let u = User { name: "Michael".into() };

    println!("{}", u.name());  // 调用方法
    println!("{}", u.name);    // 访问字段
}

可以看到,字段里有个name,方法依然可以命名成name

为啥不会冲突呢?

这个取决于调用的时候用不用括号

如果不带括号,就是访问字段

如果带括号,就是访问方法

  • u.name:没有括号,访问字段

  • u.name():有括号,调用方法

关联函数

关联函数其实就是这个类型的构造器,我想要在用这个类型构造出实例,就是用这个构造器

这玩意是挂在类型上的函数,没有self

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    // 关联函数:`new` 的第一个参数不是 self
    fn new(x: f64, y: f64, radius: f64) -> Circle {
        Circle { x, y, radius }
    }

    // 方法:有 &self
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radius * self.radius
    }
}

来看这段,里面的new就是关联函数,注意!规定就是用new

let c = Circle::new(0.0, 0.0, 1.0); // 调用关联函数,使用 ::
let a = c.area();                   // 调用方法,使用 .

通过这个new构建的实例就可以使用这个结构体里的方法了

他的特点就是

  • 定义在impl里

  • 没有self参数

  • 调用时用类型名::函数名()

最后区别一下

方法 = 拿着一个实例来掉的函数
关联函数 = 挂在「类型命名空间」里的普通函数

枚举也能使用方法

enum TrafficLight {
    Red,
    Yellow,
    Green,
}

impl TrafficLight {
    fn duration(&self) -> u32 {
        match self {
            TrafficLight::Red => 60,
            TrafficLight::Yellow => 3,
            TrafficLight::Green => 45,
        }
    }
}

fn main() {
    let light = TrafficLight::Red;
    println!("{}", light.duration());     // 像结构体一样用 .
}