【Rust学习】内存安全探秘:变量的所有权、引用与借用
字数 1047 2025-08-11 17:40:19

Rust内存安全机制:所有权、引用与借用详解

一、Rust语言概述

Rust是一种高效、可靠的通用高级语言,由Mozilla开发,最早发布于2014年9月。其核心特性包括:

  • 高性能:Rust速度惊人且内存利用率极高,没有运行时和垃圾回收
  • 可靠性:丰富的类型系统和所有权模型保证内存安全和线程安全
  • 生产力:出色的文档、友好的编译器、清晰的错误提示和一流的工具链

Rust在Linux内核开发中获得了原生支持(Linux 6.1起),这标志着它已成为系统编程的重要选择。Google Android团队也广泛使用Rust,实践证明它能显著减少内存安全漏洞。

二、所有权机制

2.1 所有权基本规则

Rust通过所有权系统管理内存,编译时检查以下规则:

  1. Rust中的每一个值都有一个所有者(owner)
  2. 值在任一时刻有且只有一个所有者
  3. 当所有者(变量)离开作用域,这个值将被丢弃

2.2 栈(Stack)与堆(Heap)

  • :后进先出结构,存储已知固定大小的数据,操作快速
  • :缺乏组织,存储编译时大小未知或可能变化的数据,通过指针访问

2.3 String类型示例

字符串字面值(硬编码)与String类型(堆分配)的区别:

let s = "hello";  // 字符串字面值,不可变
let mut s = String::from("hello");  // String类型,可变
s.push_str(", world!");

2.4 变量与数据交互

移动(Move)

let s1 = String::from("hello");
let s2 = s1;  // s1被移动到s2,s1不再有效

移动操作只复制指针、长度和容量(栈数据),不复制堆数据,避免浅拷贝问题。

克隆(Clone)

let s1 = String::from("hello");
let s2 = s1.clone();  // 深度复制堆数据

拷贝(Copy)

简单标量类型(整数、布尔、浮点、字符等)实现Copy trait,赋值后原变量仍有效:

let x = 5;
let y = x;  // x仍然有效

2.5 所有权与函数

函数传参会转移所有权,返回值也可转移所有权:

fn main() {
    let s = String::from("hello");
    takes_ownership(s);  // s的值移动进函数
    
    let x = 5;
    makes_copy(x);  // x是Copy的,之后仍可用
}

fn takes_ownership(some_string: String) {
    println!("{}", some_string);
}  // some_string离开作用域,drop被调用

fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
}  // some_integer离开作用域,无特殊操作

三、引用与借用

3.1 基本引用

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // 创建引用
    println!("Length: {}", len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}  // s离开作用域,但因为它不拥有所有权,不丢弃指向的数据

3.2 可变引用

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

3.3 引用规则

  1. 在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用
  2. 引用必须总是有效的

3.4 悬垂引用

Rust编译器防止创建悬垂引用:

fn dangle() -> &String {  // 错误!
    let s = String::from("hello");
    &s
}  // s离开作用域被丢弃,返回的引用无效

正确做法是直接返回String:

fn no_dangle() -> String {
    let s = String::from("hello");
    s
}

四、Slice类型

Slice允许引用集合中一段连续的元素序列,而不引用整个集合。

4.1 字符串Slice

let s = String::from("hello world");
let hello = &s[0..5];    // "hello"
let world = &s[6..11];   // "world"

简化语法:

let slice = &s[..2];     // 从开始到索引2
let slice = &s[3..];     // 从索引3到结束
let slice = &s[..];      // 整个字符串

4.2 改进的first_word函数

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

4.3 其他类型Slice

let a = [1, 2, 3, 4, 5];
let slice = &a[1..3];  // 类型为&[i32]
assert_eq!(slice, &[2, 3]);

五、总结

Rust的所有权、借用和slice机制提供了以下优势:

  1. 编译时确保内存安全,无需垃圾回收
  2. 避免数据竞争和悬垂指针等常见问题
  3. 通过编译器检查而非运行时开销保证安全性
  4. 提供灵活的数据访问方式(如slice)而不牺牲安全性

这些特性使Rust成为系统编程的理想选择,特别是在需要高性能和高可靠性的场景中。

Rust内存安全机制:所有权、引用与借用详解 一、Rust语言概述 Rust是一种高效、可靠的通用高级语言,由Mozilla开发,最早发布于2014年9月。其核心特性包括: 高性能 :Rust速度惊人且内存利用率极高,没有运行时和垃圾回收 可靠性 :丰富的类型系统和所有权模型保证内存安全和线程安全 生产力 :出色的文档、友好的编译器、清晰的错误提示和一流的工具链 Rust在Linux内核开发中获得了原生支持(Linux 6.1起),这标志着它已成为系统编程的重要选择。Google Android团队也广泛使用Rust,实践证明它能显著减少内存安全漏洞。 二、所有权机制 2.1 所有权基本规则 Rust通过所有权系统管理内存,编译时检查以下规则: Rust中的每一个值都有一个所有者(owner) 值在任一时刻有且只有一个所有者 当所有者(变量)离开作用域,这个值将被丢弃 2.2 栈(Stack)与堆(Heap) 栈 :后进先出结构,存储已知固定大小的数据,操作快速 堆 :缺乏组织,存储编译时大小未知或可能变化的数据,通过指针访问 2.3 String类型示例 字符串字面值(硬编码)与String类型(堆分配)的区别: 2.4 变量与数据交互 移动(Move) 移动操作只复制指针、长度和容量(栈数据),不复制堆数据,避免浅拷贝问题。 克隆(Clone) 拷贝(Copy) 简单标量类型(整数、布尔、浮点、字符等)实现Copy trait,赋值后原变量仍有效: 2.5 所有权与函数 函数传参会转移所有权,返回值也可转移所有权: 三、引用与借用 3.1 基本引用 3.2 可变引用 3.3 引用规则 在任意给定时间, 要么 只能有一个可变引用, 要么 只能有多个不可变引用 引用必须总是有效的 3.4 悬垂引用 Rust编译器防止创建悬垂引用: 正确做法是直接返回String: 四、Slice类型 Slice允许引用集合中一段连续的元素序列,而不引用整个集合。 4.1 字符串Slice 简化语法: 4.2 改进的first_ word函数 4.3 其他类型Slice 五、总结 Rust的所有权、借用和slice机制提供了以下优势: 编译时确保内存安全,无需垃圾回收 避免数据竞争和悬垂指针等常见问题 通过编译器检查而非运行时开销保证安全性 提供灵活的数据访问方式(如slice)而不牺牲安全性 这些特性使Rust成为系统编程的理想选择,特别是在需要高性能和高可靠性的场景中。