前言 Rust 连续多年被 Stack Overflow 评为「最受喜爱的编程语言」。它的核心卖点是在不依赖垃圾回收器的情况下保证内存安全 。实现这一奇迹的关键机制就是——所有权系统 。
为什么需要所有权 C/C++ 的内存管理之痛 在 C 语言中,内存管理全由程序员手动控制:
1 2 3 4 5 6 char * buffer = malloc (1024 );free (buffer);
垃圾回收的代价 Java、Go 等语言使用垃圾回收器 (GC),但:
Stop-the-world 暂停影响延迟
运行时开销
不适合系统编程场景
Rust 的第三条路 Rust 通过编译时检查 的所有权规则,在编译阶段就消除了内存错误,同时不产生运行时开销。
所有权规则 三条铁律 1 2 3 4 5 6 7 8 9 10 11 12 let s1 = String ::from ("hello" );let s2 = s1;{ let s = String ::from ("hello" ); }
Move 语义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fn main () { let s1 = String ::from ("hello" ); takes_ownership (s1); let x = 5 ; makes_copy (x); } fn takes_ownership (s: String ) { println! ("{}" , s); } fn makes_copy (n: i32 ) { println! ("{}" , n); }
引用与借用 不可变引用 1 2 3 4 5 6 7 8 9 10 fn main () { let s1 = String ::from ("hello" ); let len = calculate_length (&s1); println! ("'{}' 的长度是 {}" , s1, len); } fn calculate_length (s: &String ) -> usize { s.len () }
可变引用 1 2 3 4 5 6 7 8 9 10 fn main () { let mut s = String ::from ("hello" ); change (&mut s); println! ("{}" , s); } fn change (s: &mut String ) { s.push_str (", world" ); }
引用的规则限制 1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let mut s = String ::from ("hello" ); let r1 = &s; let r2 = &s; println! ("{} and {}" , r1, r2); let r3 = &mut s; println! ("{}" , r3); }
核心规则:在任意给定时间,要么只能有一个可变引用,要么只能有任意数量的不可变引用。
生命周期 生命周期是 Rust 编译器用来确保所有借用都是有效的一种标注机制。
生命周期标注语法 1 2 3 4 5 6 fn longest <'a >(x: &'a str , y: &'a str ) -> &'a str { if x.len () > y.len () { x } else { y } }
结构体中的生命周期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct Excerpt <'a > { part: &'a str , } fn main () { let novel = String ::from ("Call me Ishmael. Some years ago..." ); let first_sentence = novel.split ('.' ) .next () .expect ("Could not find a '.'" ); let excerpt = Excerpt { part: first_sentence, }; println! ("{}" , excerpt.part); }
生命周期省略规则 Rust 编译器有三条省略规则,让大多数情况下不需要显式标注:
1 2 3 4 规则 1:每个引用参数都有各自的生命周期参数 规则 2:如果只有一个输入生命周期,它被赋给所有输出 规则 3:如果方法有 &self 或 &mut self 参数, self 的生命周期被赋给所有输出
常用数据结构 String vs &str 1 2 3 4 5 6 7 let s1 = String ::from ("hello" );let s2 = "world" .to_string ();let s3 : &str = "hello world" ;let s4 : &str = &s1[0 ..3 ];
Vec 动态数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let mut v : Vec <i32 > = Vec ::new ();v.push (1 ); v.push (2 ); v.push (3 ); let v2 = vec! [1 , 2 , 3 , 4 , 5 ];for i in &v2 { println! ("{}" , i); } for i in &mut v { *i += 50 ; }
HashMap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 use std::collections::HashMap;let mut scores = HashMap::new ();scores.insert (String ::from ("Blue" ), 10 ); scores.insert (String ::from ("Yellow" ), 50 ); let team = String ::from ("Blue" );match scores.get (&team) { Some (score) => println! ("{} team: {}" , team, score), None => println! ("{} team not found" , team), } scores.entry (String ::from ("Blue" )).or_insert (60 );
错误处理 Result 类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 use std::fs::File;use std::io::{self , Read};fn read_username_from_file () -> Result <String , io::Error> { let f = File::open ("hello.txt" ); let mut f = match f { Ok (file) => file, Err (e) => return Err (e), }; let mut s = String ::new (); match f.read_to_string (&mut s) { Ok (_) => Ok (s), Err (e) => Err (e), } } fn read_username_from_file_short () -> Result <String , io::Error> { let mut s = String ::new (); File::open ("hello.txt" )?.read_to_string (&mut s)?; Ok (s) }
Option vs Result
类型
用途
Some/Ok
None/Err
Option
值可能为空
Some(value)
None
Result
操作可能失败
Ok(value)
Err(error)
实战:简单 CLI 工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 use std::env;use std::fs;use std::process;struct Config { query: String , filename: String , } impl Config { fn new (args: &[String ]) -> Result <Config, &'static str > { if args.len () < 3 { return Err ("参数不足:需要搜索内容和文件路径" ); } let query = args[1 ].clone (); let filename = args[2 ].clone (); Ok (Config { query, filename }) } } fn main () { let args : Vec <String > = env::args ().collect (); let config = Config::new (&args).unwrap_or_else (|err| { eprintln! ("错误: {}" , err); process::exit (1 ); }); let contents = fs::read_to_string (&config.filename) .unwrap_or_else (|err| { eprintln! ("读取文件失败: {}" , err); process::exit (1 ); }); for line in search (&config.query, &contents) { println! ("{}" , line); } } fn search <'a >(query: &str , contents: &'a str ) -> Vec <&'a str > { contents .lines () .filter (|line| line.contains (query)) .collect () }
总结 Rust 的所有权系统初学时可能让人沮丧,但一旦理解了它背后的设计哲学——在编译时而不是运行时保证内存安全 ——你就会发现这套系统既是挑战,也是 Rust 最强大的武器。
学习曲线大致是:所有权 → 借用 → 生命周期 → 泛型 + trait → 并发。按这个顺序,一个概念接一个概念地攻克,最终你会爱上这门语言。