# Rust编程语言入门
---
核心规则:
fn main() {
// 所有权移动
let s1 = String::from("hello");
let s2 = s1; // s1的所有权移动到s2
// println!("{}", s1); // 编译错误!s1已失效
// 借用(引用)
let s3 = String::from("world");
let len = calculate_length(&s3); // 不可变借用
println!("'{}'的长度是{}", s3, len);
// 可变借用
let mut s4 = String::from("Rust");
change(&mut s4); // 可变借用
println!("修改后:{}", s4);
}
fn calculate_length(s: &String) -> usize {
s.len() // 借用不获取所有权
}
fn change(s: &mut String) {
s.push_str(" is awesome");
}
关键技术对比
| 特性 | 所有权转移 | 不可变借用 | 可变借用 |
|------|-----------|-----------|---------|
| 语法 | let b = a; | &T | &mut T |
| 所有权 | 转移 | 保留 | 保留 |
| 可修改 | 仅新所有者可修改 | 不可修改 | 可修改 |
| 同时存在 | 仅一个所有者 | 多个 | 仅一个 |
---
模块2:结构体与枚举
原理讲解
结构体(struct)和枚举(enum)是Rust中定义自定义数据类型的主要方式。结构体将相关数据组合在一起,而枚举允许一个值可以是几种不同类型中的一种。
代码示例
// 定义结构体
struct Book {
title: String,
author: String,
pages: u32,
is_available: bool,
}
// 定义枚举
enum BookStatus {
Available,
Borrowed(String), // 带数据的变体
Reserved { date: String, user: String }, // 具名字段变体
}
// 为结构体实现方法
impl Book {
fn new(title: &str, author: &str, pages: u32) -> Book {
Book {
title: String::from(title),
author: String::from(author),
pages,
is_available: true,
}
}
fn display(&self) {
println!("《{}》 by {}", self.title, self.author);
}
}
fn main() {
let book = Book::new("Rust编程之道", "张三", 350);
book.display();
let status = BookStatus::Borrowed(String::from("李四"));
// 后续可用模式匹配处理status
}
---
模块3:模式匹配
原理讲解
模式匹配是Rust中强大的控制流工具,可以解构数据结构、匹配枚举变体、处理Option<T>和Result<T, E>类型。match表达式必须覆盖所有可能性,否则编译器会报错。
代码示例
enum Coin {
Penny,
Nickel,
Dime,
Quarter(String), // 带州名的Quarter
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("幸运便士!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("来自{}的25美分", state);
25
}
}
}
// 使用if let简化单模式匹配
fn main() {
let coin = Coin::Quarter(String::from("California"));
let value = value_in_cents(coin);
println!("价值:{}美分", value);
// 处理Option<T>
let some_value = Some(3);
if let Some(x) = some_value {
println!("值为:{}", x);
}
}
---
模块4:错误处理与Cargo包管理
原理讲解
Rust的错误处理分为可恢复错误(Result<T, E>)和不可恢复错误(panic!)。Result枚举有两个变体:Ok(T)表示成功,Err(E)表示失败。
Cargo是Rust的构建系统和包管理器,负责依赖管理、编译、测试和文档生成。
代码示例
use std::fs::File;
use std::io::{self, Read};
// 使用Result处理错误
fn read_username_from_file() -> Result<String, io::Error> {
let mut file = File::open("hello.txt")?; // ?运算符自动传播错误
let mut username = String::new();
file.read_to_string(&mut username)?;
Ok(username)
}
// Cargo.toml示例
// [package]
// name = "my_cli_tool"
// version = "0.1.0"
// edition = "2021"
//
// [dependencies]
// serde = { version = "1.0", features = ["derive"] }
// clap = "4.0"
fn main() {
match read_username_from_file() {
Ok(name) => println!("用户名:{}", name),
Err(e) => eprintln!("读取失败:{}", e),
}
}
---
三、实操步骤:开发一个文件统计CLI工具
步骤1:创建新项目
# 创建项目
cargo new file_stats
cd file_stats
步骤2:添加依赖
编辑Cargo.toml:
[package]
name = "file_stats"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "4.0", features = ["derive"] }
步骤3:编写主程序
src/main.rs:
use clap::Parser;
use std::fs::File;
use std::io::{BufRead, BufReader};
#[derive(Parser)]
#[command(name = "file_stats")]
#[command(about = "统计文件的行数、单词数和字符数")]
struct Cli {
#[arg(help = "要统计的文件路径")]
file_path: String,
}
fn main() {
let cli = Cli::parse();
let file = File::open(&cli.file_path).expect("无法打开文件");
let reader = BufReader::new(file);
let mut line_count = 0;
let mut word_count = 0;
let mut char_count = 0;
for line in reader.lines() {
let line = line.expect("读取行失败");
line_count += 1;
word_count += line.split_whitespace().count();
char_count += line.chars().count();
}
println!("文件: {}", cli.file_path);
println!("行数: {}", line_count);
println!("单词数: {}", word_count);
println!("字符数: {}", char_count);
}
步骤4:构建和运行
# 构建项目
cargo build --release
# 运行程序
./target/release/file_stats Cargo.toml
预期输出:
文件: Cargo.toml
行数: 10
单词数: 25
字符数: 180
---
四、常见问题与故障排查
问题1:编译错误“borrow of moved value”
原因:尝试使用已移动所有权的变量
解决:使用引用(&)而不是移动,或克隆值
let s1 = String::from("hello");
let s2 = s1.clone(); // 克隆而不是移动
println!("{} {}", s1, s2); // 正常工作
问题2:同时存在多个可变引用
原因:Rust不允许同时存在多个可变引用
解决:使用代码块限制作用域
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1作用域结束
let r2 = &mut s; // 现在可以创建新可变引用
问题3:`match`没有覆盖所有情况
原因:模式匹配必须穷举所有可能性
解决:使用通配符_处理剩余情况
match x {
1 => println!("one"),
2 => println!("two"),
_ => println!("其他"), // 通配符分支
}
问题4:Cargo依赖下载失败
原因:网络问题或Cargo镜像配置
解决:配置国内镜像源
# 在~/.cargo/config.toml中添加
[source.crates-io]
replace-with = 'ustc'
[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"
---
五、总结与扩展
重点知识回顾
1. 所有权三原则:每个值只有一个所有者,所有权可移动,所有者离开作用域值被释放
2. 借用规则:任意时刻要么有一个可变引用,要么有多个不可变引用
3. 模式匹配:match必须穷举,if let适合单模式匹配
4. 错误处理:优先使用Result<T, E>处理可恢复错误,?运算符简化错误传播
5. Cargo工作流:cargo new创建项目,cargo build编译,cargo run运行
实战最佳实践
- 编写代码时优先使用`String`和`Vec<T>`,它们拥有所有权
- 函数参数优先使用引用(`&T`)而不是获取所有权
- 使用`cargo clippy`进行代码审查,`cargo fmt`格式化代码
- 为每个`Result`和`Option`值显式处理错误,避免使用`unwrap()`
- 使用`#[derive(Debug)]`为自定义类型自动实现调试输出
进一步学习方向
1. 进阶概念:生命周期标注、智能指针(Box<T>、Rc<T>、RefCell<T>)、闭包与迭代器
2. 并发编程:std::thread、std::sync模块、async/await异步编程
3. Web开发:使用Actix-web或Rocket框架构建Web应用
4. 系统编程:FFI(外部函数接口)、裸指针操作、内联汇编
5. 推荐资源:
- 《Rust程序设计语言》(The Rust Programming Language)
- 《Rust实战》(Rust in Action)
- Rust官方文档:https://doc.rust-lang.org/
- Rustlings练习:https://github.com/rust-lang/rustlings