Rust

2025年07月29日更新 7 人订阅
原价: ¥ 6 限时优惠
专栏简介 Rust编程语言之错误处理 Rust 语言之 flod Rust编程语言之Cargo、Crates.io详解 Rust编程语言之枚举与模式匹配 Rust语言 - 接口设计的建议之受约束(Constrained) Rust编程语言之无畏并发 Rust语言 - 接口设计的建议之灵活(flexible) Rust语言 - 接口设计的建议之显而易见(Obvious) Rust语言 - 接口设计的建议之不意外(unsurprising) Rust 实战:构建实用的 CLI 工具 HTTPie Rust编程语言学习之高级特性 Rust内存管理揭秘:深度剖析指针与智能指针 解决Rust中数组和切片的编译时大小问题 《Rust编程之道》学习笔记一 Rust Async 异步编程 简易教程 使用 Async Rust 构建简单的 P2P 节点 Rust编程语言入门之模式匹配 Rust async 编程 Rust编程语言之编写自动化测试 Rust编程语言之函数式语言特性:迭代器和闭包 《Rust编程之道》学习笔记二 Rust Tips 比较数值 使用 Rust 开发一个微型游戏 Rust编程初探:深入理解Struct结构体 深入理解Rust中的内存管理:栈、堆与静态内存详解 深入理解 Rust 结构体:经典结构体、元组结构体和单元结构体的实现 深入掌握 Rust 结构体:从模板到实例化的完整指南 深入理解Rust中的结构体:逻辑与数据结合的实战示例 深入理解 Rust 枚举:从基础到实践 掌握Rust字符串的精髓:String与&str的最佳实践 全面解析 Rust 模块系统:实战案例与应用技巧 Rust 中的 HashMap 实战指南:理解与优化技巧 掌握Rust模式匹配:从基础语法到实际应用 Rust 中的面向对象编程:特性与实现指南 深入理解 Rust 的 Pin 和 Unpin:理论与实践解析 Rust Trait 与 Go Interface:从设计到实战的深度对比 从零开始:用 Rust 和 Axum 打造高效 Web 应用 Rust 错误处理详解:掌握 anyhow、thiserror 和 snafu Rust 如何优雅实现冒泡排序 链表倒数 K 节点怎么删?Python/Go/Rust 实战 用 Rust 玩转数据存储:JSON 文件持久化实战 Rust实战:打造高效字符串分割函数 如何高效学习一门技术:从知到行的飞轮效应 Rust 编程入门:Struct 让代码更优雅 Rust 编程:零基础入门高性能开发 用 Rust 写个猜数游戏,编程小白也能上手! Rust 入门教程:变量到数据类型,轻松掌握! 深入浅出 Rust:函数、控制流与所有权核心特性解析 从零开始:用 Rust 和 Axum 打造高效 Web 服务 Rust 集合类型解析:Vector、String、HashMap 深入浅出Rust:泛型、Trait与生命周期的硬核指南 Rust实战:博物馆门票限流系统设计与实现 用 Rust 打造高性能图片处理服务器:从零开始实现类似 Thumbor 的功能 Rust 编程入门实战:从零开始抓取网页并转换为 Markdown 深入浅出 Rust:高效处理二进制数据的 Bytes 与 BytesMut 实战 Rust智能指针:解锁内存管理的进阶之道 用 Rust 打造命令行利器:从零到一实现 mini-grep 解锁Rust代码组织:轻松掌握Package、Crate与Module Rust 所有权:从内存管理到生产力释放 深入解析 Rust 的面向对象编程:特性、实现与设计模式 Rust + Protobuf:从零打造高效键值存储项目 bacon 点燃 Rust:比 cargo-watch 更爽的开发体验 用 Rust 打造微型游戏:从零开始的 Flappy Dragon 开发之旅 函数式编程的Rust之旅:闭包与迭代器的深入解析与实践 探索Rust编程之道:从设计哲学到内存安全的学习笔记 精读《Rust编程之道》:吃透语言精要,彻底搞懂所有权与借用 Rust 避坑指南:搞定数值比较,别再让 0.1 + 0.2 != 0.3 困扰你! 告别 Vec!掌握 Rust bytes 库,解锁零拷贝的真正威力 告别竞态条件:基于 Axum 和 Serde 的 Rust 并发状态管理最佳实践 Rust 异步编程实践:从 Tokio 基础到阻塞任务处理模式 Rust 网络编程实战:用 Tokio 手写一个迷你 TCP 反向代理 (minginx) 保姆级教程:Zsh + Oh My Zsh 终极配置,让你的 Ubuntu 终端效率倍增 不止于后端:Rust 在 Web 开发中的崛起之路 (2024数据解读) Rust核心利器:枚举(Enum)与模式匹配(Match),告别空指针,写出优雅健壮的代码 Rust 错误处理终极指南:从 panic! 到 Result 的优雅之道 想用 Rust 开发游戏?这份超详细的入门教程请收好! 用 Rust 实现 HTTPie:一个现代 CLI 工具的构建过程 Rust 异步实战:从0到1,用 Tokio 打造一个高性能并发聊天室 深入 Rust 核心:彻底搞懂指针、引用与智能指针 Rust 生产级后端实战:用 Axum + sqlx 打造高性能短链接服务 深入 Rust 内存模型:栈、堆、所有权与底层原理 Rust 核心概念解析:引用、借用与内部可变性 掌握 Rust 核心:生命周期与借用检查全解析 Rust 内存布局深度解析:从对齐、填充到 repr 属性 Rust Trait 分派机制:静态与动态的抉择与权衡

掌握 Rust 核心:生命周期与借用检查全解析

掌握Rust核心:生命周期与借用检查全解析“生命周期”和“借用检查器”无疑是Rust学习路上的两大核心挑战,它们是Rust实现内存安全、告别垃圾回收的基石,却也常常让初学者望而生畏,面对一堆编译错误不知所措。别担心,这篇文章就是为你准备的。我们将系统性地剖析生命周期的本质,理解借用检

掌握 Rust 核心:生命周期与借用检查全解析

“生命周期”和“借用检查器”无疑是 Rust 学习路上的两大核心挑战,它们是 Rust 实现内存安全、告别垃圾回收的基石,却也常常让初学者望而生畏,面对一堆编译错误不知所措。

别担心,这篇文章就是为你准备的。我们将系统性地剖析生命周期的本质,理解借用检查器“保守”的工作原则。从最基础的概念出发,逐步深入到泛型生命周期、生命周期型变(Variance)等高级主题。无论你是初学者还是希望巩固知识的开发者,本文都将帮助你建立一个清晰、完整的知识框架,让你自信地驾驭 Rust 的所有权系统。

本文深入探讨 Rust 的核心概念——生命周期。我们将从生命周期的基本定义和借用检查器的工作原理讲起,逐步深入到泛型生命周期和生命周期型变(Variance)等高级主题,旨在帮助开发者彻底理解 Rust 如何在编译期保证引用的有效性,从而掌握其内存安全的基石,写出更安全、高效的代码。

生命周期

生命周期(Lifetime)

  • 官方教程中提到:
    • Rust 里每个引用都有生命周期,它就是引用保持合法作用域(scope),大多数时候是隐式和推断出来的
  • 对某个变量取得引用时生命周期开始,当变量移动或离开作用域时生命周期结束
  • 生命周期:对于某个引用来说,它必须保持合法的一个代码区域的名称
    • 生命周期通常与作用域重合,但也不一定

借用检查器(Borrow Checker)

  • 每当具有某个生命周期 ‘a 的引用被使用,借用检查器都会检查 ’a 是否还存活
    • 追踪路径直到 ‘a 开始(获得引用)的地方
    • 从这开始,检查沿着路径是否存在冲突
    • 保证引用指向一个可安全访问的值
use rand::prelude::*;

fn main() {
  let mut x = Box::new(42);
  let r = &x; // (1) 'a
  if random::<f32>() > 0.5 {
    *x = 84; // (2)
  } else {
    println!("{}", r); // (3) 'a
  }
}
// (4)
  • 生命周期很复杂:
  • 生命周期有“漏洞”
fn main() {
  let mut x = Box::new(42);

  let mut x = &x; // (1) a'
  for i in 0..100 {
    println!("{}", z); // (2) a'
    x = Box::new(i); // (3) 
    z = &x; // (4) a'
  }
  println!("{}", z); // a'
}
  • 借用检查器是保守的:
    • 如果不确定某个借用是否合法,借用检查器就会拒绝该借用
  • 借用检查器有时需要帮助来理解某个借用 为什么是合法的:
    • 这也是 Unsafe Rust 存在的部分原因

泛型生命周期

  • 有时需要在自己的类型里存储引用:
    • 这些引用都有生命周期,以便借用检查器检查合法性
    • 例如:在该类型方法中返回引用,且存活比 self 的长
  • Rust 允许你基于一个或多个生命周期将类型的定义泛型化

泛型生命周期 - 两点提醒

  • 如果类型实现了 Drop,那么丢弃类型时,就被记作是使用了类型所泛型的生命周期或类型
    • 类型实例要被 drop 了,在 drop 之前,借用检查器会检查看是否仍然合法去使用你类型的泛型生命周期,因为你在 drop 里的代码可能会用到这些引用
  • 如果类型没有实现 Drop,那么丢弃类型的时候不会当作使用,可以忽略类型内的引用
  • 类型可泛型多个生命周期,但通常会不必要的让类型签名更复杂:
    • 只有类型包含多个引用时,你才应该使用多个生命周期参数
    • 并且它方法返回的引用只应绑定到其中一个引用的生命周期
strurc StrSplit<'s, 'p> {
  delimiter: &'p str,
  document: &'s str,
}

impl<'s, 'p> Iterator for StrSplit<'s, 'p> {
  type Item = &'s str;

  fn next(&mut self) -> Option<Self::Item> {
    todo!()
  }
}

fn str_before(s: &str, c: char) -> Option<&str> {
  StrSplit {
    document: s,
    delimiter: &c.to_string(),
  }
  .next()
}

生命周期 Variance

  • Variance:
    • 哪些类型是其它类型的“子类”
    • 什么时候“子类”可以替换“超类”(反之亦然)
  • 通常来说:
    • 如果 A 是 B 的子类,那么 A 至少和 B 一样有用
    • Rust 的例子:
    • 如果函数接收 &'a str 的参数,那么就可以传入 &'statci str 的参数
    • 'static 是 'a 的 “子类”,因为 'static 至少跟任何 'a 存活的一样长

生命周期 三种 Variance

  • 所有的类型都有 Variance:
    • 定义了哪些类似类型可以用在该类型的位置上
  • 三种 Variance:
    • covariant:某类型只能用“子类型”替代
    • 例如:&'static T 可替代 &'a T
    • invariant:必须提供指定的类型
    • 例如:&mut T,对于 T 来说就是 invariant 的
    • contravariant:函数对参数的要求越低,参数可发挥的作用越大
// let x1: &'static str;  // more useful, lives longer
// let x2: &'a str;  // less useful, lives shorter

// fn take_func1(&'static str1)  // stricter, so less useful
// fn take_func2(&'a str2)  // less strict, more useful

多生命周期与 Variance

struct MuStr<'a, 'b> {
  s: &'a mut &'b str,
}

fn main() {
  let mut r = "hello";  // &'static str => &'a str
  *MutStr {s: &mut r}.s = "world";
  println!("{}", r);
}

总结

本文详细梳理了 Rust 中至关重要的生命周期(Lifetime)概念。我们从以下几个方面进行了探讨:

  1. 基本概念:明确了生命周期是引用保持合法的“代码区域”,并了解了借用检查器(Borrow Checker)如何通过追踪生命周期来防止悬垂引用,确保内存安全。

  2. 泛型生命周期:学习了如何在结构体和函数签名中使用泛型生命周期(如 struct StrSplit<'s, 'p>),这使得自定义类型能够安全地持有和返回引用。

  3. 生命周期型变 (Variance):介绍了协变(Covariant)、不变(Invariant)和逆变(Contravariant)这三种型变规则,解释了为什么 &'static str 可以作为 &'a str 使用,加深了对生命周期替换规则的理解。

总而言之,生命周期是 Rust 独特的、在编译时强制执行的内存安全机制。虽然初看时语法和概念较为复杂,但一旦掌握,它将成为你编写高性能、高可靠性程序的强大工具。彻底理解生命周期,是每一位 Rustacean 从入门到精通的必经之路。

参考

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论