Rust 基础教程:从零开始的系统编程之旅
前言
Rust 是一门追求性能、可靠性和生产力的系统编程语言。自 2015 年发布 1.0 版本以来,连续多年在 Stack Overflow 开发者调查中被评为"最受喜爱的编程语言"。本教程面向有基本编程经验的读者,带你快速上手 Rust 核心概念。
目录
一、环境搭建
安装 Rust
推荐使用 rustup 工具链管理器,一条命令搞定:
Windows :下载并运行 rustup-init.exe
macOS / Linux:
bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
安装完成后验证:
bash
rustc --version
cargo --version
推荐 IDE
- VS Code + rust-analyzer 插件(最主流)
- IntelliJ IDEA / CLion + Rust 插件
- RustRover(JetBrains 官方 Rust IDE)
二、Hello World 与 Cargo
创建项目
bash
cargo new hello_rust
cd hello_rust
生成的目录结构:
hello_rust/
├── Cargo.toml # 项目配置文件(类似 package.json)
└── src/
└── main.rs # 程序入口
src/main.rs 中的代码:
rust
fn main() {
println!("Hello, world!");
}
编译与运行
bash
cargo build # 编译(debug 模式)
cargo run # 编译并运行
cargo build --release # 生产模式编译(优化)
Cargo.toml 基础
toml
[package]
name = "hello_rust"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = "1.0" # 添加第三方依赖
三、变量与可变性
Rust 变量默认不可变,这是安全性的基石之一。
rust
fn main() {
let x = 5; // 不可变绑定
// x = 6; // ❌ 编译错误!cannot assign twice to immutable variable
let mut y = 10; // 可变绑定,使用 mut 关键字
y = 20; // ✅ 合法
println!("x = {}, y = {}", x, y);
}
常量
rust
const MAX_POINTS: u32 = 100_000; // 必须标注类型,命名全大写
变量遮蔽(Shadowing)
rust
fn main() {
let x = 5;
let x = x + 1; // 用 let 重新声明,遮蔽旧值
let x = x * 2; // 可以改变类型
println!("x = {}", x); // 12
}
四、数据类型
标量类型
| 类型 | 示例 | 说明 |
|---|---|---|
i8 / i16 / i32 / i64 / i128 |
let a: i32 = 42; |
有符号整数 |
u8 / u16 / u32 / u64 / u128 |
let b: u8 = 255; |
无符号整数 |
isize / usize |
let c: usize = 100; |
平台相关整数 |
f32 / f64 |
let d: f64 = 3.14; |
浮点数(默认 f64) |
bool |
let e: bool = true; |
布尔值 |
char |
let f: char = '🦀'; |
Unicode 字符(4字节) |
复合类型
rust
// 元组(Tuple)
let tup: (i32, f64, char) = (500, 6.4, 'R');
let (x, y, z) = tup; // 解构
println!("{}, {}, {}", tup.0, tup.1, tup.2); // 索引访问
// 数组(固定长度,栈上分配)
let arr: [i32; 5] = [1, 2, 3, 4, 5];
let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
println!("{}", arr[0]);
Vector(动态数组)
rust
let mut v: Vec<i32> = Vec::new();
v.push(1);
v.push(2);
let v2 = vec![1, 2, 3, 4]; // 宏创建
for i in &v2 {
println!("{}", i);
}
五、所有权与借用
这是 Rust 最核心的特性------无需垃圾回收器即可保证内存安全。
所有权规则
- Rust 中每个值都有一个所有者(owner)。
- 同一时刻,一个值只能有一个所有者。
- 当所有者离开作用域,值被自动释放(调用
drop)。
rust
fn main() {
let s1 = String::from("hello"); // s1 拥有字符串
let s2 = s1; // 所有权转移(move),s1 失效
// println!("{}", s1); // ❌ s1 已失效
println!("{}", s2); // ✅
}
克隆(深拷贝)
rust
let s1 = String::from("hello");
let s2 = s1.clone(); // 深拷贝,s1 仍可用
println!("s1 = {}, s2 = {}", s1, s2);
引用与借用
rust
fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // 不可变引用(借用)
println!("'{}' 的长度是 {}", s, len); // s 仍可用
}
fn calculate_length(s: &String) -> usize {
s.len()
}
可变引用
rust
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // "hello, world"
}
fn change(s: &mut String) {
s.push_str(", world");
}
引用的规则(防止数据竞争):
- 同一时刻,要么一个可变引用 ,要么多个不可变引用,不能同时存在。
- 引用必须始终有效(不能悬垂)。
rust
let mut s = String::from("hello");
let r1 = &s; // 不可变引用
let r2 = &s; // 不可变引用
// let r3 = &mut s; // ❌ 不能同时存在可变引用
println!("{}, {}", r1, r2);
六、结构体与枚举
结构体
rust
// 定义
struct User {
username: String,
email: String,
active: bool,
}
// 实例化
let user1 = User {
email: String::from("alice@example.com"),
username: String::from("alice"),
active: true,
};
// 字段更新语法
let user2 = User {
email: String::from("bob@example.com"),
..user1 // 其余字段继承 user1
};
为结构体实现方法
rust
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
// 关联函数(构造器)
fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
// 方法(&self 不可变借用)
fn area(&self) -> u32 {
self.width * self.height
}
// 方法(&mut self 可变借用)
fn set_width(&mut self, width: u32) {
self.width = width;
}
}
fn main() {
let mut rect = Rectangle::new(30, 50);
println!("面积: {}", rect.area()); // 1500
rect.set_width(40);
println!("新面积: {}", rect.area()); // 2000
}
枚举与模式匹配
rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn process(msg: &Message) {
match msg {
Message::Quit => println!("退出"),
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
Message::Write(text) => println!("消息: {}", text),
Message::ChangeColor(r, g, b) => println!("颜色: ({}, {}, {})", r, g, b),
}
}
Option 枚举
Rust 没有 null,使用 Option<T> 表达"可能有值":
rust
fn find_user(id: u32) -> Option<String> {
if id == 1 {
Some(String::from("Alice"))
} else {
None
}
}
fn main() {
match find_user(1) {
Some(name) => println!("找到: {}", name),
None => println!("未找到用户"),
}
// if let 简写
if let Some(name) = find_user(2) {
println!("找到: {}", name);
} else {
println!("未找到用户");
}
}
七、错误处理
Rust 将错误分为两类:可恢复错误 (Result)和不可恢复错误 (panic!)。
Result 类型
rust
use std::fs::File;
use std::io::{self, Read};
fn read_file(path: &str) -> Result<String, io::Error> {
let mut file = File::open(path)?; // ? 运算符:出错则提前返回 Err
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
match read_file("hello.txt") {
Ok(content) => println!("文件内容:\n{}", content),
Err(e) => eprintln!("读取失败: {}", e),
}
}
panic! 宏
rust
fn main() {
// panic!("程序崩溃!"); // 不可恢复,立即终止
let v = vec![1, 2, 3];
// v[99]; // 索引越界,触发 panic
}
unwrap 与 expect
rust
let f = File::open("hello.txt").unwrap(); // 出错则 panic
let f = File::open("hello.txt").expect("无法打开文件"); // 带自定义消息的 panic
自定义错误类型
rust
#[derive(Debug)]
enum AppError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
}
impl From<std::io::Error> for AppError {
fn from(e: std::io::Error) -> Self { AppError::Io(e) }
}
impl From<std::num::ParseIntError> for AppError {
fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) }
}
八、实战:命令行工具
写一个统计文件中词频的小工具,综合运用前面学到的知识。
创建项目
bash
cargo new wordcount
cd wordcount
编辑 Cargo.toml
toml
[package]
name = "wordcount"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "4", features = ["derive"] }
完整代码 (src/main.rs)
rust
use clap::Parser;
use std::collections::HashMap;
use std::fs;
/// 统计文本文件中单词的出现频率
#[derive(Parser, Debug)]
#[command(version, about)]
struct Args {
/// 输入文件路径
#[arg(short, long)]
input: String,
/// 显示前 N 个高频词
#[arg(short, long, default_value_t = 10)]
top: usize,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
let content = fs::read_to_string(&args.input)?;
let mut word_count: HashMap<String, usize> = HashMap::new();
for word in content
.to_lowercase()
.split(|c: char| !c.is_alphanumeric())
.filter(|w| !w.is_empty())
{
*word_count.entry(word.to_string()).or_insert(0) += 1;
}
let mut sorted: Vec<(&String, &usize)> = word_count.iter().collect();
sorted.sort_by(|a, b| b.1.cmp(a.1));
println!("=== 词频统计 Top {} ===\n", args.top);
println!("{:<20} {:>8}", "单词", "次数");
println!("{}", "-".repeat(30));
for (word, count) in sorted.iter().take(args.top) {
println!("{:<20} {:>8}", word, count);
}
Ok(())
}
运行
bash
cargo run -- --input sample.txt --top 10
这个例子涵盖了:结构体派生宏、错误处理 (?)、HashMap、迭代器、泛型、trait 对象等多个核心概念。
九、总结
| 概念 | 要点 |
|---|---|
| 所有权 | 每值一主,离开作用域自动释放 |
| 借用 | 引用不转移所有权,可变借用互斥 |
| 生命周期 | 编译器静态保证引用有效性 |
| 枚举 + match | 表达力极强,模式匹配穷尽检查 |
| Result / Option | 无 null、无异常,编译时强制处理错误 |
| Trait | 类似接口的共享行为抽象 |
| 零成本抽象 | 高级语法编译后与手写底层代码性能相当 |