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()); // 像结构体一样用 .
}