Rust 所有权与核心概念完整指南
2859 字
14 分钟
Rust 所有权与核心概念完整指南
1. 字符串字面量 &str
let s = "hello"; // 类型:&str特点:
- 存储在栈上(实际在二进制文件的只读内存中)
- 不可变
- 编译时已知大小
- 是对固定字符串的引用(借用)
- 零开销,最高效
2. String 类型
let s = String::from("hello"); // 类型:String特点:
- 数据存储在堆上
- 可变(可以修改)
- 运行时动态分配内存
- 拥有数据的所有权
- 离开作用域时自动释放内存
3. 两者对比
| 特性 | &str | String |
|---|---|---|
| 存储位置 | 栈(只读内存) | 堆 |
| 可变性 | 不可变 | 可变 |
| 大小 | 编译时已知 | 运行时动态 |
| 所有权 | 借用 | 拥有 |
| 性能 | 最快 | 稍慢(堆分配) |
4. 相互转换
// &str -> Stringlet s1 = "hello".to_string();let s2 = String::from("hello");
// String -> &strlet s3 = String::from("hello");let s4 = &s3[0..3]; // "hel"let s5 = &s3; // 自动转换第二部分:可变性(mut 关键字)
1. 基本概念
let x = 5; // 不可变(默认)let mut y = 5; // 可变y = 10; // ✅ 可以修改// x = 10; // ❌ 编译错误!2. 为什么默认不可变?
- 防止意外修改
- 更容易推理代码
- 编译器可以做更多优化
- 内存安全保证
3. 可变性的应用场景
// 1. 变量绑定let mut count = 0;count += 1;
// 2. 函数参数fn increment(mut num: i32) { num += 1;}
// 3. String 的可变性let mut s = String::from("hello");s.push_str(" world"); // 可以修改
// 4. 结构体字段struct User { name: String, age: u32,}let mut user = User { name: "Alice".to_string(), age: 30 };user.age = 31; // 整个结构体必须是 mut4. mut 与所有权的关系
let mut s = String::from("hello");let t = &mut s; // 可变引用t.push_str(" world");
// let u = &mut s; // ❌ 不能同时有多个可变引用第三部分:内存分配(栈 vs 堆)
1. 栈(Stack)
特点:
- 后进先出(LIFO)
- 存储已知大小的数据
- 分配和释放极快
- 函数调用时自动管理
存储内容:
let a = 5; // 整数let b = true; // 布尔let c = 'A'; // 字符let d = [1, 2, 3]; // 数组(固定大小)let e = (4, 5.6); // 元组(固定大小)let f = "hello"; // 字符串字面量(&str)2. 堆(Heap)
特点:
- 动态分配
- 存储未知大小或需要增长的数据
- 分配较慢
- 需要手动或自动(Rust)管理
存储内容:
let s = String::from("hello"); // 字符串内容let v = vec![1, 2, 3]; // 向量内容let b = Box::new(42); // Box 数据3. 内存布局图解
let s = String::from("hello");栈(Stack) 堆(Heap)┌─────────────┐ ┌─────────────┐│ s (指针) │────────>│ "hello" ││ 长度: 5 │ │ h e l l o ││ 容量: 5 │ └─────────────┘└─────────────┘4. 可变性与存储位置无关
| 变量 | 可变性 | 存储位置 | 示例 |
|---|---|---|---|
let x = 5 | 不可变 | 栈 | 整数、布尔、字符 |
let mut x = 5 | 可变 | 栈 | 同上,但可改值 |
let s = String::from("hi") | 不可变 | 栈指针→堆数据 | 字符串内容在堆上 |
let mut s = String::from("hi") | 可变 | 栈指针→堆数据 | 可以修改堆上内容 |
第四部分:Box<T> 智能指针
1. 什么是 Box?
Box<T> 是 Rust 提供的堆内存分配器:
let x = 5; // 存储在栈上let y = Box::new(5); // 存储在堆上,栈上只存指针2. 内存布局
let boxed = Box::new(42);栈(Stack) 堆(Heap)┌─────────────┐ ┌─────────────┐│ boxed │────────>│ 42 ││ (指针) │ │ (实际数据) │└─────────────┘ └─────────────┘3. Box 的主要用途
(1) 递归数据结构
enum List { Cons(i32, Box<List>), // Box 让递归成为可能 Nil,}
// 如果不用 Box// enum List {// Cons(i32, List), // ❌ 编译错误:无限大小!// Nil,// }(2) 特征对象(多态)
trait Animal { fn sound(&self);}
struct Dog;impl Animal for Dog { fn sound(&self) { println!("汪汪!"); }}
struct Cat;impl Animal for Cat { fn sound(&self) { println!("喵喵!"); }}
let animals: Vec<Box<dyn Animal>> = vec![ Box::new(Dog), Box::new(Cat),];(3) 让枚举大小固定
enum Data { Small(Vec<u8>), Large(Box<Vec<u8>>), // Box 让枚举大小固定}4. Box 的自动解引用
let mut boxed = Box::new(vec![1, 2, 3]);boxed.push(4); // ✅ 自动解引用,直接使用boxed[0] = 42; // ✅ 自动解引用
// 特殊情况需要手动解引用let mut b = Box::new(5);*b = 10; // ✅ 修改值需要 *5. Box vs 普通变量
| 特性 | 普通变量 | Box |
|---|---|---|
| 存储位置 | 栈上 | 堆上 |
| 内存大小 | 实际数据大小 | 8字节(指针) |
| 所有权 | 直接拥有数据 | 拥有堆上数据 |
| 性能 | 更快 | 稍慢(间接访问) |
第五部分:Vec<T> 泛型集合
1. 基本概念
Vec<u8> // 存储 u8 类型(无符号8位整数)Vec<i32> // 存储 i32 类型Vec<String> // 存储 String 类型Vec<f64> // 存储 f64 类型2. 类型大小
| 类型 | 单个元素大小 | 范围 |
|---|---|---|
u8 | 1 字节 | 0 ~ 255 |
i32 | 4 字节 | -2,147,483,648 ~ 2,147,483,647 |
f64 | 8 字节 | ±1.8e308(双精度) |
String | 24 字节(指针+长度+容量) | 动态 |
3. 内存占用
// Vec 本身在栈上占 24 字节// 堆上数据 = 元素个数 × 每个元素大小
let v1: Vec<u8> = vec![0; 1000]; // 堆上占 1000 字节let v2: Vec<i32> = vec![0; 1000]; // 堆上占 4000 字节4. 重要概念
// Vec 的数据在堆上,不是栈上!let huge_array = vec![0; 1000000]; // 数据在堆上
// 转移所有权只复制指针(高效)let vec1 = vec![0; 1000000];let vec2 = vec1; // 只复制了3个字节(指针、长度、容量)// vec1 不再可用
// clone 会复制所有数据(慢)let vec3 = vec![0; 1000000];let vec4 = vec3.clone(); // 复制了100万个元素第六部分:枚举(enum)
1. Rust enum vs Python enum
Python:简单枚举值
from enum import Enumclass Color(Enum): RED = 1 GREEN = 2Rust:强大的标签联合(可以携带数据)
enum Color { Red, Green, Blue,}
enum Message { Quit, // 无数据 Move { x: i32, y: i32 }, // 结构体数据 Write(String), // 单个数据 ChangeColor(u8, u8, u8), // 元组数据}2. 枚举变体
enum List { Cons(i32, Box<List>), // 变体1:包含值和下一个节点 Nil, // 变体2:空链表}
// 使用变体let node = List::Cons(5, Box::new(List::Nil));3. 使用 use 简化路径
// 完整路径let list = List::Cons(1, Box::new(List::Nil));
// 导入后简化use List::{Cons, Nil};let list = Cons(1, Box::new(Nil));4. 匹配枚举
match small_data { Data::Small(vec) => println!("小数据:{:?}", vec), Data::Large(vec) => println!("大数据:长度 {}", vec.len()),}第七部分:Cons 链表
1. Cons 是什么?
Cons 来自 Lisp 语言,是 “Constructor” 的缩写:
- Cons = 一个节点,包含当前值和指向下一个节点的指针
- Nil = 空链表(结束标志)
2. 链表定义
enum List { Cons(i32, Box<List>), // 值 + 指向下一个节点的指针 Nil, // 空}
// 链表:1 -> 2 -> 3 -> Nil// Cons(1, Cons(2, Cons(3, Nil)))3. 构建链表
// 从尾部开始构建let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
// 逐步构建let tail = Cons(3, Box::new(Nil));let mid = Cons(2, Box::new(tail));let head = Cons(1, Box::new(mid));4. 内存布局
栈 堆┌──────────────┐ ┌──────────────┐│ list (头部) │──────────>│ Cons(1) │└──────────────┘ │ next ──────┐│ └─────────────┴│ ▼ ┌──────────────┐ │ Cons(2) │ │ next ──────┐│ └─────────────┴│ ▼ ┌──────────────┐ │ Cons(3) │ │ next ──────┐│ └─────────────┴│ ▼ ┌──────────────┐ │ Nil │ └──────────────┘5. 更优雅的构建方式
// 使用标准库use std::collections::LinkedList;let mut list = LinkedList::new();list.extend([1, 2, 3]);
// 使用 Vec(推荐!)let list = vec![1, 2, 3]; // 90% 的情况用 Vec 就够了第八部分:操作符 . vs ::
1. 核心规则
// . 用于实例(已经创建的对象)// :: 用于类型/模块(命名空间)2. . 点操作符
用于实例的字段和方法:
struct Person { name: String, age: u32,}
impl Person { fn greet(&self) { println!("你好,我是{}", self.name); }}
let p = Person { name: "Alice".to_string(), age: 30 };p.greet(); // ✅ 调用实例方法println!("{}", p.name); // ✅ 访问实例字段3. :: 双冒号操作符
用于类型级别的东西:
// 1. 关联函数(类似静态方法)let s = String::from("hello");let v = Vec::new();
// 2. 枚举变体let c = Color::Red;
// 3. 模块路径use std::collections::HashMap;
// 4. 关联常量struct Config;impl Config { const VERSION: u32 = 1;}println!("{}", Config::VERSION);4. 完整对比表
| 操作符 | 使用场景 | 示例 |
|---|---|---|
. | 访问实例字段 | person.name |
. | 调用实例方法 | person.greet() |
. | 访问元组索引 | tuple.0 |
:: | 调用关联函数 | String::from("hi") |
:: | 访问关联常量 | MyStruct::VERSION |
:: | 访问枚举变体 | Color::Red |
:: | 模块路径 | std::collections::HashMap |
5. 从 Python 角度理解
class Config: DEFAULT = 100 # 类属性(类似关联常量)
def __init__(self, value): self.value = value # 实例属性
print(Config.DEFAULT) # 类属性访问config = Config(42)print(config.value) # 实例属性访问struct Config { value: i32,}
impl Config { const DEFAULT: i32 = 100; // 关联常量}
println!("{}", Config::DEFAULT); // :: 访问let config = Config { value: 42 };println!("{}", config.value); // . 访问第九部分:解引用运算符 *
1. 基本概念
* 是解引用运算符,用于”跟随指针拿到数据”。
2. 使用场景
// 1. 栈上的引用let x = 42;let r = &x;println!("{}", *r); // 解引用,拿到 42
// 2. 堆上的数据(Box)let b = Box::new(100);println!("{}", *b); // 解引用,拿出 1003. 自动解引用
Rust 会自动解引用,大多数情况不需要手动 *:
let mut boxed = Box::new(vec![1, 2, 3]);boxed.push(4); // ✅ 自动解引用println!("{}", boxed[0]); // ✅ 自动解引用4. 何时需要手动 *
// 1. 修改 Box 的值let mut b = Box::new(5);*b = 10; // ✅ 必须用 *
// 2. 取出所有权let b = Box::new(String::from("hello"));let s = *b; // ✅ 取出堆上的 String
// 3. 自定义类型的访问struct Point { x: i32, y: i32 }let p = Box::new(Point { x: 1, y: 2 });println!("{}", (*p).x); // 可以,但 p.x 更常见5. 解引用总结
| 操作符 | 作用 | 何时需要 |
|---|---|---|
* | 解引用(跟随指针) | 修改 Box 的值、取出所有权 |
. | 自动解引用 | 调用方法时自动处理 |
[] | 索引(自动解引用) | 访问 Vec 等容器元素 |
第十部分:完整代码示例
// 1. 字符串类型fn string_examples() { let s1 = "hello"; // &str,栈上 let s2 = String::from("world"); // String,堆上
let mut s3 = String::from("hello"); s3.push_str(" world"); // 可以修改 println!("{}", s3);}
// 2. Box 使用fn box_examples() { let b = Box::new(42); println!("{}", *b); // 解引用
let mut b2 = Box::new(10); *b2 = 20; // 修改 println!("{}", b2); // 自动解引用}
// 3. 枚举和链表enum List { Cons(i32, Box<List>), Nil,}
fn list_examples() { use List::{Cons, Nil};
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
// 遍历 let mut current = &list; while let Cons(value, next) = current { print!("{} -> ", value); current = next; } println!("Nil");}
// 4. Vec 使用fn vec_examples() { let mut v = vec![1, 2, 3]; v.push(4); v[0] = 10; println!("{:?}", v); // [10, 2, 3, 4]
// Vec 数据在堆上 let big = vec![0; 1000000]; let moved = big; // 所有权转移,只复制指针 // println!("{}", big[0]); // ❌ big 已失效}
// 5. 结构体和关联常量struct Config { pub value: i32,}
impl Config { const DEFAULT: i32 = 100;
fn new(value: i32) -> Self { Config { value } }
fn get_value(&self) -> i32 { self.value }}
fn struct_examples() { println!("{}", Config::DEFAULT); // :: 访问关联常量
let config = Config::new(42); // :: 调用关联函数 println!("{}", config.value); // . 访问字段 println!("{}", config.get_value()); // . 调用方法}
// 6. main 函数fn main() { string_examples(); box_examples(); list_examples(); vec_examples(); struct_examples();}总结:核心要点速查
内存管理
- 栈:固定大小,快速,自动管理
- 堆:动态大小,较慢,需要显式管理(Rust 自动清理)
所有权规则
- 每个值都有一个所有者
- 同一时间只能有一个所有者
- 所有者离开作用域时值被丢弃
可变性
- 默认不可变(
let) - 需要可变时用
mut - 可变性与存储位置无关
字符串
&str:不可变,栈上,借用String:可变,堆上,拥有所有权
智能指针
Box<T>:堆分配,拥有所有权- 用于递归类型、特征对象、固定枚举大小
集合
Vec<T>:动态数组,数据在堆上LinkedList:双向链表,标准库提供
操作符
.:实例级别(字段、方法):::类型级别(关联函数、常量、枚举变体)*:解引用(跟随指针)
枚举
- 强大的标签联合
- 可以携带数据
- 变体可以用
use导入
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
Rust 所有权与核心概念完整指南
https://guoxin.wiki/posts/rust-ownership-guide/相关文章智能推荐
1
Poetry 命令大全及更新指南
编程语言Python 依赖管理工具 Poetry 的常用命令速查:项目初始化、依赖管理、虚拟环境与发布流程。
2
Windows 11 上封锁 Claude Code 出网:防火墙 + DNS + hosts 三层实战
运维与工具用防火墙规则、DNS 重定向和 hosts 三层手段封锁 Claude Code 的出网流量,并给出一套可验证、可回滚的完整脚本与自检流程。
3
React 学习 Day1:基础语法与整体框架(含 JS 关键基础)
前端建立对 React 的整体认识,并补齐编写 React 所必需的 JavaScript 语法基础:组件化、Props/State、JSX 与常用概念。
4
2026 年 9 月复盘:换赛道的第一个月
月度复盘转岗到智能体方向的第一个月:两条产品线的架构设计从提出到通过评审,中间踩的坑、补的课,以及对『先写文档还是先写代码』的新认识。
5
Linux screen 命令详解
运维与工具screen 的常用命令与快捷键速查:会话创建、分离恢复、窗口操作,SSH 断线也不怕任务中断。
随机文章随机推荐














