Rust借用规则:如何避免编译错误

简介: Rust的借用机制允许安全共享数据而不转移所有权。本文详解三大借用规则(唯一可变借用、不可变/可变借用互斥、引用必须有效),结合代码示例讲解常见错误与实用技巧,助你掌握内存安全的核心实践。

所有权是Rust的核心,但在实际开发中,我们更多时候需要的是共享数据而不是转移所有权。这就引出了Rust的另一个重要概念——借用(Borrowing)。本文详细介绍了Rust的借用规则,以及如何在实际开发中避免常见的编译错误。

什么是借用

借用就是创建一个指向值的引用,而不获取其所有权。引用就像一个指针,但它有更严格的规则来保证安全。

fn calculate_length(s: &String) -> usize {
   
    s.len()
}

fn main() {
   
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("'{}' 的长度是 {}", s1, len);
}

在这个例子中,&s1创建了一个指向s1的引用,函数calculate_length借用了s1但没有获取所有权。当函数执行完毕后,引用离开作用域,但它指向的值并不会被丢弃。

借用的规则

Rust的借用规则非常严格,主要有三条:

  1. 同一时刻只能有一个可变借用
  2. 可变借用和不可变借用不能同时存在
  3. 借用必须有效(引用的对象在借用期间不能被销毁)

规则一:同一时刻只能有一个可变借用

let mut s = String::from("hello");
let r1 = &mut s;
// let r2 = &mut s; // 编译错误!同一时刻只能有一个可变借用

这条规则防止了数据竞争。如果多个可变引用同时指向同一个数据,就可能在没有同步机制的情况下发生并发修改。

规则二:可变借用和不可变借用不能同时存在

let mut s = String::from("hello");
let r1 = &s; // 不可变借用
// let r2 = &mut s; // 编译错误!不能同时有不可变和可变借用

这条规则保证了读取数据时的数据一致性。如果在读取数据的同时允许修改,就可能导致读取到不一致的数据。

规则三:借用必须有效

// 错误示例:悬垂引用
fn dangling_reference() -> &String {
   
    let s = String::from("hello");
    &s // 返回指向s的引用,但s即将离开作用域
}

// 正确做法:返回所有权
fn not_dangling() -> String {
   
    let s = String::from("hello");
    s // 返回所有权,调用者负责管理内存
}

这条规则防止了悬垂引用,即引用指向已经被释放的内存。

实际开发中的借用技巧

技巧一:使用不可变借用进行只读操作

fn print_strings(strings: &[String]) {
   
    for s in strings {
   
        println!("{}", s);
    }
}

fn main() {
   
    let strings = vec![String::from("hello"), String::from("world")];
    print_strings(&strings);
    // strings仍然有效
}

技巧二:使用可变借用进行修改操作

fn append_world(s: &mut String) {
   
    s.push_str(" world");
}

fn main() {
   
    let mut s = String::from("hello");
    append_world(&mut s);
    println!("{}", s);
}

技巧三:在函数中同时使用多个借用

fn compare_strings(s1: &String, s2: &String) -> bool {
   
    s1.len() > s2.len()
}

fn main() {
   
    let s1 = String::from("hello");
    let s2 = String::from("world");
    let result = compare_strings(&s1, &s2);
    println!("s1比s2长吗?{}", result);
}

技巧四:使用切片进行部分借用

fn print_part(s: &str) {
   
    println!("{}", s);
}

fn main() {
   
    let s = String::from("hello world");
    let part = &s[0..5];
    print_part(part);
}

常见错误及解决方案

错误一:尝试修改不可变借用

// 错误
let s = String::from("hello");
let r = &s;
r.push_str(" world");

// 解决方案:使用可变借用
let mut s = String::from("hello");
let r = &mut s;
r.push_str(" world");

错误二:在借用期间修改原始数据

// 错误
let mut s = String::from("hello");
let r = &s;
s.push_str(" world");

// 解决方案:先修改再借用
let mut s = String::from("hello");
s.push_str(" world");
let r = &s;

错误三:返回局部变量的引用

// 错误
fn get_string() -> &String {
   
    let s = String::from("hello");
    &s
}

// 解决方案:返回所有权
fn get_string() -> String {
   
    let s = String::from("hello");
    s
}

总结

借用规则是Rust内存安全的重要保障。虽然这些规则在初期可能会让人感到束缚,但它们从根本上消除了数据竞争、悬垂引用等常见的内存安全问题。

在实际开发中,合理使用不可变借用和可变借用,可以在保证内存安全的前提下,实现高效的数据共享和修改。希望这篇文章能帮助你更好地理解和应用Rust的借用规则!

目录
相关文章
|
23天前
|
Linux 程序员 数据格式
【2026最新】Notepad++下载、安装和使用一篇搞定(附中文版安装包)
Notepad++ 是一款免费开源、轻量高效的 Windows 文本编辑器,支持 C/Python/HTML 等 80+ 语言语法高亮、代码折叠、正则替换、编码转换及插件扩展,专为程序员与文本处理用户打造,完美替代系统记事本。(239字)
|
8天前
|
人工智能 缓存 安全
Claude Code 封号真实原因曝光,这次彻底不装了,直接针对国内开发者的账号下手?
Claude Code 封号潮背后:逆向扒出客户端隐写区域标记,Anthropic 政策收紧叠加 DeepSeek 7 月涨价,国产替代更紧迫。
|
13天前
|
人工智能 JSON 自然语言处理
让教学更智慧:用阿里云百炼工作流,自动生成中小学教材内容#小有可为#有温度的AI
通过可视化工作流编排,将大模型推理能力转化为标准化的教学内容生成引擎。教师只需输入教材标题和适用学段,即可自动获得结构完整、符合课程标准的章节内容,大幅降低备课门槛,助力教育资源均衡化。
499 127
|
17天前
|
存储 人工智能 监控
QoderWork完全指南:从入门到精通,把“AI实习生”变成你的全能工作搭档
阿里云2026年推出的桌面端AI工作助手QoderWork,不止聊天,更可动手干活:本地运行、安全可控,支持文件整理、数据分析、PPT生成、网页开发等;内置专家套件、多Agent协作与自定义Skills,让AI真正成为你身边的“AI实习生”。
|
8天前
|
人工智能 安全 程序员
终于,Claude Code 封号的原因被曝光了!竟然针对中国用户,植入隐形代码?!
通俗易懂地揭秘 Claude Code 封号的手段,分享一些自己对 AI 编程困境的思考,Codex、Cursor、DeepSeek、智谱 GLM、甚至是豆包,都有所行动了
473 1
|
9天前
|
人工智能 安全 Cloud Native
Higress 新发布:AI Gateway 能力增强,Gateway API 及其推理扩展持续打磨
增强 AI 网关能力,持续打磨 Gateway API 及其推理扩展。
395 126
|
7天前
|
人工智能 编解码 物联网
2026 最新Stable Diffusion 本地部署教程 下载安装使用详细图解(含官网安装包)
Stable Diffusion(SD)是2022年发布的开源文生图模型,由Stability AI等联合开发。支持文生图、图生图、局部重绘等,依托VAE降低算力需求,可在消费级显卡运行。本文提供秋葉aaaki制作的Windows整合包(含图形界面与插件),开箱即用,零配置启动。