Skip to content

一文彻底搞懂 Rust 中的 self、&self、mut self、&mut self

· 8 min

作为一个 Rust 新手,相信你一定被这四种 self 的写法搞得晕头转向。别慌,今天我用最通俗易懂的方式,结合实际代码示例,让你彻底理解它们的区别和使用场景。

前言#

在学习 Rust 的过程中,方法定义中的 self 参数是绕不开的核心概念。很多初学者看到 self&selfmut self&mut self 这四种写法时都会一脸懵逼:

今天就让我们一次性把这些问题都解决掉!

核心概念速览#

在深入代码之前,先理解一个关键概念:所有权系统

写法含义所有权变化使用场景
self获取所有权转移消费对象、转换对象
&self不可变借用无变化只读访问
&mut self可变借用无变化修改对象
mut self可变所有权转移获取并修改对象

实战演示:图书管理系统#

让我们通过一个图书管理的例子来逐一分析:

#[derive(Debug)]
struct Book {
title: String,
pages: u32,
available: bool,
}

1. self - 我要”吃掉”这本书#

当方法签名是 self 时,意味着这个方法会消费掉调用的对象。

impl Book {
// 归还图书 - 消费掉Book对象
fn return_to_library(self) {
println!("📚 《{}》已归还图书馆", self.title);
// self 在这里被销毁,不再可用
}
// 转换为页数 - 典型的消费模式
fn into_page_count(self) -> u32 {
println!("正在统计《{}》的页数...", self.title);
self.pages
}
}
fn main() {
let book = Book {
title: "Rust权威指南".to_string(),
pages: 500,
available: true,
};
let pages = book.into_page_count();
println!("总页数: {}", pages);
// ❌ 编译错误!book 已经被消费了
// println!("{:?}", book);
}

使用场景

2. &self - 我只是”看看”这本书#

这是最常用的形式,只需要读取数据,不修改也不消费。

impl Book {
// 获取书名 - 只读访问
fn get_title(&self) -> &str {
&self.title
}
// 检查是否可借 - 只读判断
fn is_available(&self) -> bool {
self.available
}
// 获取详细信息 - 只读访问多个字段
fn get_info(&self) -> String {
format!(
"📖 《{}》- {} 页 [{}]",
self.title,
self.pages,
if self.available { "可借" } else { "已借出" }
)
}
// 比较两本书的页数
fn compare_pages(&self, other: &Book) -> std::cmp::Ordering {
self.pages.cmp(&other.pages)
}
}
fn main() {
let book1 = Book {
title: "Rust权威指南".to_string(),
pages: 500,
available: true,
};
let book2 = Book {
title: "Rust实战".to_string(),
pages: 400,
available: false,
};
// 可以多次调用不可变方法
println!("{}", book1.get_info());
println!("{}", book2.get_info());
// 比较页数
match book1.compare_pages(&book2) {
std::cmp::Ordering::Greater => println!("📚 第一本书更厚"),
std::cmp::Ordering::Less => println!("📚 第二本书更厚"),
std::cmp::Ordering::Equal => println!("📚 两本书一样厚"),
}
// ✅ book1 和 book2 仍然可用
println!("book1 仍可用: {}", book1.get_title());
}

使用场景

3. &mut self - 我要”修改”这本书#

当需要修改对象状态时使用,不会获取所有权,但需要独占访问。

impl Book {
// 借出图书
fn borrow_out(&mut self) -> Result<(), &'static str> {
if !self.available {
return Err("图书已被借出");
}
self.available = false;
println!("📤 《{}》已借出", self.title);
Ok(())
}
// 归还图书
fn return_back(&mut self) {
self.available = true;
println!("📥 《{}》已归还", self.title);
}
// 更新页数(比如发现统计错误)
fn update_pages(&mut self, new_pages: u32) {
let old_pages = self.pages;
self.pages = new_pages;
println!("📝 《{}》页数从 {} 更新为 {}", self.title, old_pages, new_pages);
}
// 修改书名
fn rename(&mut self, new_title: String) {
let old_title = std::mem::replace(&mut self.title, new_title);
println!("📝 书名从《{}》改为《{}", old_title, self.title);
}
}
fn main() {
let mut book = Book { // 必须声明为 mut
title: "Rust入门".to_string(),
pages: 200,
available: true,
};
// 修改操作
book.borrow_out().unwrap();
println!("当前状态: {}", book.get_info());
book.return_back();
book.update_pages(250);
book.rename("Rust进阶".to_string());
// ✅ book 仍然可用,只是状态被修改了
println!("最终状态: {}", book.get_info());
}

重要提醒

4. mut self - 我要”拿走并改造”这本书#

这种用法相对少见,表示获取所有权并且可以修改。

impl Book {
// 升级图书 - 获取所有权并修改后返回
fn upgrade_edition(mut self, version: u32) -> Book {
self.title = format!("{} (第{}版)", self.title, version);
self.pages += 50; // 新版本通常内容更多
println!("📈 图书已升级: {}", self.title);
self
}
// 转换为电子书格式
fn convert_to_ebook(mut self) -> EBook {
// 修改一些属性后转换
self.pages = (self.pages as f32 * 0.8) as u32; // 电子书页数通常更少
EBook {
title: self.title,
pages: self.pages,
file_size_mb: self.pages / 10, // 简单计算文件大小
}
}
}
#[derive(Debug)]
struct EBook {
title: String,
pages: u32,
file_size_mb: u32,
}
fn main() {
let book = Book {
title: "Rust编程".to_string(),
pages: 300,
available: true,
};
// 升级版本
let upgraded_book = book.upgrade_edition(2);
// ❌ book 已经被消费,不可再用
println!("{:?}", upgraded_book);
// 转换为电子书
let ebook = upgraded_book.convert_to_ebook();
// ❌ upgraded_book 也被消费了
println!("{:?}", ebook);
}

实际开发中的选择策略#

优先级排序#

  1. &self - 默认选择,90% 的情况都用这个
  2. &mut self - 需要修改状态时
  3. self - 转换或消费对象时
  4. mut self - 特殊场景,较少使用

常见模式#

impl Book {
// 构造器 - 返回新对象
fn new(title: String, pages: u32) -> Self {
Book { title, pages, available: true }
}
// Getter - 使用 &self
fn title(&self) -> &str { &self.title }
fn pages(&self) -> u32 { self.pages }
// Setter - 使用 &mut self
fn set_available(&mut self, available: bool) {
self.available = available;
}
// Builder 模式 - 使用 mut self
fn with_pages(mut self, pages: u32) -> Self {
self.pages = pages;
self
}
// 转换方法 - 使用 self
fn into_title(self) -> String {
self.title
}
}
// Builder 模式的使用
fn main() {
let book = Book::new("基础教程".to_string(), 100)
.with_pages(200); // 链式调用
println!("{:?}", book);
}

总结#

理解这四种 self 的关键在于所有权思维

掌握了这些概念,你就能写出更安全、更高效的 Rust 代码。记住,Rust 的所有权系统看似复杂,实际上是在编译时帮你避免了运行时的各种内存问题。