这是一篇从零开始的详细指南,涵盖核心概念、所有权系统、实战示例等内容。
🦀 Rust 语言入门完全指南:从零开始掌握系统级编程
"Rust 是一门赋予每个人构建可靠且高效软件能力的语言。"
目录
- [为什么选择 Rust?](#为什么选择 Rust? "#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9-rust")
- 环境搭建
- 基础语法速览
- 核心概念:所有权系统
- 结构体与枚举
- 错误处理
- [实战:构建 CLI 工具](#实战:构建 CLI 工具 "#%E5%AE%9E%E6%88%98%E6%9E%84%E5%BB%BA-cli-%E5%B7%A5%E5%85%B7")
- 学习资源推荐
为什么选择 Rust?
Rust 自 2015 年发布 1.0 版本以来,连续八年被 Stack Overflow 评为最受开发者喜爱的编程语言。它的核心优势在于:
| 特性 | 说明 |
|---|---|
| 内存安全 | 编译时检查,无需垃圾回收器 (GC) |
| 零成本抽象 | 高级特性不牺牲运行时性能 |
| 并发安全 | 编译期防止数据竞争 |
| 现代工具链 | Cargo 包管理、内置测试、文档生成 |
适用场景:系统编程、WebAssembly、嵌入式开发、高性能网络服务、区块链基础设施等。
环境搭建
安装 Rustup(官方推荐方式)
bash
# Linux/macOS
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Windows
# 下载并运行 https://win.rustup.rs/
安装完成后,验证环境:
bash
rustc --version # 查看编译器版本
cargo --version # 查看包管理器版本
第一个程序:Hello, World!
创建项目:
bash
cargo new hello_rust
cd hello_rust
src/main.rs 内容:
rust
fn main() {
println!("Hello, Rustaceans! 🦀");
}
运行:
bash
cargo run
💡 提示 :
Cargo是 Rust 的构建系统和包管理器,类比 Python 的 pip + setuptools,或 Node.js 的 npm。
基础语法速览
变量与可变性
rust
fn main() {
// 不可变变量(默认)
let x = 5;
// x = 6; // 错误!不能修改不可变变量
// 可变变量
let mut y = 5;
y = 6; // ✅ 合法
// 常量(编译期确定,必须标注类型)
const MAX_POINTS: u32 = 100_000;
// 遮蔽(Shadowing)- 允许同名变量
let spaces = " ";
let spaces = spaces.len(); // 类型从 &str 变为 usize
}
数据类型
rust
fn main() {
// 标量类型
let a: i32 = -42; // 有符号整数
let b: u64 = 100; // 无符号整数
let c: f64 = 3.14159; // 浮点数
let d: bool = true; // 布尔值
let e: char = 'ℤ'; // Unicode 字符(4字节)
// 复合类型:元组
let tup: (i32, f64, &str) = (500, 6.4, "hi");
let (x, y, z) = tup; // 解构
let five_hundred = tup.0; // 索引访问
// 复合类型:数组(固定长度)
let arr = [1, 2, 3, 4, 5];
let first = arr[0];
// 动态数组(Vec,在堆上分配)
let mut vec = vec![1, 2, 3];
vec.push(4);
}
函数与控制流
rust
fn main() {
let result = add(5, 3);
println!("5 + 3 = {}", result);
// if 表达式(注意:不是语句,返回值为表达式结果)
let number = 6;
let condition = if number % 2 == 0 { "偶数" } else { "奇数" };
// 循环
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // 返回值
}
};
// while 循环
while counter > 0 {
println!("{}!", counter);
counter -= 1;
}
// for 循环(推荐,安全且高效)
for number in 1..4 {
println!("{}!", number);
}
}
// 函数定义:参数需标注类型,返回值类型在箭头后
fn add(a: i32, b: i32) -> i32 {
a + b // 注意:无分号表示表达式返回值,等同于 return a + b;
}
核心概念:所有权系统
所有权 (Ownership) 是 Rust 最独特的特性,它让 Rust 无需垃圾回收器就能保证内存安全。
三条黄金规则
- 每个值都有一个所有者 (owner)
- 同一时间只能有一个所有者
- 所有者离开作用域,值被丢弃
所有权转移(Move)
rust
fn main() {
let s1 = String::from("hello"); // s1 拥有这个字符串
let s2 = s1; // 所有权转移给 s2
// println!("{}", s1); // ❌ 错误!s1 不再有效
// 深拷贝(Clone)
let s3 = s2.clone();
println!("s2 = {}, s3 = {}", s2, s3); // ✅ 两者都可用
}
借用(Borrowing)
使用引用 & 借用值,而不获取所有权:
rust
fn main() {
let s = String::from("hello");
// 不可变借用
let len = calculate_length(&s);
println!("'{}' 的长度是 {}", s, len); // ✅ s 仍然有效
// 可变借用
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // 输出 "hello, world"
}
fn calculate_length(s: &String) -> usize {
s.len()
} // s 在这里离开作用域,但不拥有所有权,所以不会 drop
fn change(s: &mut String) {
s.push_str(", world");
}
借用规则(编译器强制执行)
- 要么 一个可变引用
&mut T - 要么 任意数量的不可变引用
&T - 不能同时存在
rust
let mut s = String::from("hello");
let r1 = &s; // 没问题
let r2 = &s; // 没问题
let r3 = &mut s; // ❌ 大错误!不能同时拥有不可变和可变引用
println!("{}, {}, and {}", r1, r2, r3);
Slice 类型
不持有所有权的数据引用,常用于字符串切片:
rust
fn main() {
let s = String::from("hello world");
let hello = &s[0..5]; // 字符串切片
let world = &s[6..11];
// 更好的 first_word 函数
let word = first_word(&s);
println!("第一个单词是: {}", word);
}
fn first_word(s: &str) -> &str { // &str 是字符串切片类型
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
结构体与枚举
结构体定义
rust
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
fn main() {
// 创建实例
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
// 结构体更新语法
let user2 = User {
email: String::from("another@example.com"),
..user1 // 其余字段从 user1 获取
};
// 元组结构体
struct Point(i32, i32, i32);
let origin = Point(0, 0, 0);
}
方法定义
rust
#[derive(Debug)] // 自动派生 Debug trait,支持打印
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 构造函数(约定俗成叫 new,但不是关键字)
fn new(width: u32, height: u32) -> Self {
Rectangle { width, height }
}
// 方法:第一个参数是 self(&self, &mut self 或 self)
fn area(&self) -> u32 {
self.width * self.height
}
// 关联函数(静态方法):没有 self 参数
fn square(size: u32) -> Rectangle {
Rectangle { width: size, height: size }
}
}
fn main() {
let rect = Rectangle::new(30, 50);
println!("面积: {}", rect.area());
let sq = Rectangle::square(3);
println!("{:?}", sq); // 使用 Debug 格式打印
}
枚举与模式匹配
rust
enum Message {
Quit, // 无数据
Move { x: i32, y: i32 }, // 匿名结构体
Write(String), // 单个值
ChangeColor(i32, i32, i32), // 元组
}
impl Message {
fn call(&self) {
match self {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y),
Message::Write(text) => println!("Text: {}", text),
Message::ChangeColor(r, g, b) => println!("Color: {}, {}, {}", r, g, b),
}
}
}
// Option 枚举(Rust 没有 null!)
enum Option<T> {
Some(T),
None,
}
fn main() {
let some_number = Some(5);
let absent_number: Option<i32> = None;
// match 必须穷举所有可能
let coin = Message::Quit;
coin.call();
// if let 简化匹配(只关心一种模式)
if let Message::Quit = coin {
println!("是 Quit 消息");
}
}
错误处理
Rust 将错误分为两类:可恢复错误 (Result<T, E>)和不可恢复错误 (panic!)。
Result 类型
rust
use std::fs::File;
use std::io::{self, Read};
fn main() {
// 手动处理
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("打开文件失败: {:?}", error),
};
// 简写:unwrap(失败时 panic)和 expect(带自定义消息)
let f = File::open("hello.txt").expect("无法打开 hello.txt");
// ? 运算符:错误传播
let content = read_username_from_file().expect("读取失败");
}
fn read_username_from_file() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?; // 如果 Err,直接返回
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
// 更简洁的写法
fn read_username_from_file_v2() -> Result<String, io::Error> {
std::fs::read_to_string("hello.txt")
}
实战:构建 CLI 工具
让我们用 Rust 构建一个实用的命令行工具:文件内容搜索工具(简化版 grep)。
项目结构
bash
cargo new minigrep
cd minigrep
完整代码
Cargo.toml:
toml
[package]
name = "minigrep"
version = "0.1.0"
edition = "2021"
[dependencies]
colored = "2.0" # 添加彩色输出依赖
src/main.rs:
rust
use std::env;
use std::fs;
use std::process;
use colored::*;
struct Config {
query: String,
file_path: String,
ignore_case: bool,
}
impl Config {
fn build(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("用法: minigrep <查询字符串> <文件路径>");
}
let query = args[1].clone();
let file_path = args[2].clone();
let ignore_case = env::var("IGNORE_CASE").is_ok();
Ok(Config { query, file_path, ignore_case })
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::build(&args).unwrap_or_else(|err| {
eprintln!("参数错误: {}", err);
process::exit(1);
});
println!("搜索: {}", config.query.green());
println!("文件: {}", config.file_path.blue());
if let Err(e) = run(config) {
eprintln!("应用错误: {}", e);
process::exit(1);
}
}
fn run(config: Config) -> Result<(), Box<dyn std::error::Error>> {
let contents = fs::read_to_string(&config.file_path)?;
let results = if config.ignore_case {
search_case_insensitive(&config.query, &contents)
} else {
search(&config.query, &contents)
};
for line in results {
println!("{}", line.red());
}
Ok(())
}
fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
contents
.lines()
.filter(|line| line.contains(query))
.collect()
}
fn search_case_insensitive<'a>(
query: &str,
contents: &'a str,
) -> Vec<&'a str> {
let query = query.to_lowercase();
contents
.lines()
.filter(|line| line.to_lowercase().contains(&query))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn case_sensitive() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";
assert_eq!(vec!["safe, fast, productive."], search(query, contents));
}
#[test]
fn case_insensitive() {
let query = "rUsT";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
assert_eq!(
vec!["Rust:", "Trust me."],
search_case_insensitive(query, contents)
);
}
}
运行与测试
bash
# 构建并运行
cargo run -- "test" poem.txt
# 运行测试
cargo test
# 发布构建(优化性能)
cargo build --release
学习资源推荐
官方资源
- The Rust Programming Language(即 "The Book")- 最权威的学习资料
- Rust by Example - 通过实例学习
- Rustlings - 交互式小练习
中文社区
- Rust 语言圣经 (Rust Course) - 高质量中文教程
- Rust 中文社区
进阶书籍
- 《Programming Rust》 - 系统深入
- 《Rust Atomics and Locks》 - 并发编程
- 《Zero To Production In Rust》 - 后端实战
实践建议
- 先理解所有权:这是 Rust 的门槛,也是精髓。建议画内存图辅助理解。
- 善用编译器:Rust 编译器的错误提示非常友好,仔细阅读建议。
- 从项目中学:尝试用 Rust 重写你熟悉的工具,如 HTTP 客户端、爬虫等。
- 参与社区 :Rust 社区非常友好,遇到问题可以在 users.rust-lang.org 提问。
总结
Rust 的学习曲线确实比大多数语言陡峭,但这是为了换取编译期的安全保证 和运行时的零成本。一旦掌握所有权系统,你将获得:
- 不再担心空指针、野指针、数据竞争
- 高性能的代码(媲美 C/C++)
- 现代化的工具链和包管理
- 可靠的并发编程能力
正如 Rust 的口号所说:"Fearless Concurrency"(无畏并发)。开始你的 Rust 之旅吧!🦀
作者注:本文代码均在 Rust 1.75+ 版本测试通过。如有疑问或建议,欢迎在评论区留言交流。
: Stack Overflow Developer Survey 2023 - Most Loved Languages : The Rust Programming Language - doc.rust-lang.org/book/ : Rust语言圣经 - course.rs/