Rust 基础概念速查表
本文档为 Rust 新手提供项目中常见概念的快速参考。
目录
基础语法
变量声明
// 不可变变量(默认)
let x = 5;
// x = 6; // 错误!不可变变量不能重新赋值
// 可变变量
let mut y = 5;
y = 6; // 正确
// 类型标注
let z: i32 = 7;
// 常量(必须标注类型,编译时确定)
const MAX_POINTS: u32 = 100_000;
函数
// 普通函数
fn add(a: i32, b: i32) -> i32 {
a + b // 最后一个表达式是返回值(不需要 return)
}
// 带 return 的写法
fn add2(a: i32, b: i32) -> i32 {
return a + b; // 显式 return
}
// 无返回值(返回单元类型 ())
fn print_hello() {
println!("Hello!");
}
// 等价于
fn print_hello2() -> () {
println!("Hello!");
}
结构体
// 定义结构体
struct User {
username: String,
email: String,
age: u32,
}
// 实例化
let user = User {
username: String::from("alice"),
email: String::from("alice@example.com"),
age: 25,
};
// 访问字段
println!("{}", user.username);
// 为结构体实现方法
impl User {
// 关联函数(类似静态方法)
fn new(username: String, email: String) -> Self {
Self {
username,
email,
age: 18, // 默认值
}
}
// 实例方法
fn greet(&self) {
println!("Hello, {}!", self.username);
}
// 可变方法
fn set_age(&mut self, age: u32) {
self.age = age;
}
}
// 使用
let user = User::new(String::from("bob"), String::from("bob@example.com"));
user.greet();
所有权系统
Rust 最独特和重要的特性,保证内存安全。
三条规则
- 每个值都有一个所有者
- 同一时间只能有一个所有者
- 所有者离开作用域时,值被丢弃
所有权转移(Move)
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权转移给 s2
// println!("{}", s1); // 错误!s1 已经失效
println!("{}", s2); // 正确
借用(Borrowing)
let s1 = String::from("hello");
// 不可变借用(可以有多个)
let r1 = &s1;
let r2 = &s1;
println!("{} and {}", r1, r2);
// 可变借用(同时只能有一个)
let mut s2 = String::from("hello");
let r3 = &mut s2;
r3.push_str(" world");
// let r4 = &mut s2; // 错误!不能同时有两个可变借用
项目中的例子
// config/database.rs
pub async fn create_pool(database_url: &str) -> Result<MySqlPool, sqlx::Error> {
// ^^^^ 借用,不获取所有权
MySqlPoolOptions::new()
.connect(database_url) // 可以使用借用的值
.await
}
// main.rs
let db = database::create_pool(&config.database_url).await?;
// ^^^ 传递借用
// config.database_url 仍然有效
类型系统
基本类型
// 整数
let a: i32 = 42; // 32位有符号整数
let b: u64 = 100; // 64位无符号整数
let c: isize = 10; // 指针大小的有符号整数
// 浮点数
let d: f64 = 3.14; // 64位浮点数
// 布尔值
let e: bool = true;
// 字符
let f: char = 'A'; // Unicode 字符
// 字符串
let s1: &str = "hello"; // 字符串切片(不可变)
let s2: String = String::from("hello"); // 可增长字符串
Option 类型
表示值可能存在或不存在,避免空指针。
// 定义
enum Option<T> {
Some(T), // 有值
None, // 无值
}
// 使用
let some_number: Option<i32> = Some(5);
let no_number: Option<i32> = None;
// 匹配
match some_number {
Some(n) => println!("数字是: {}", n),
None => println!("没有数字"),
}
// if let 简化
if let Some(n) = some_number {
println!("数字是: {}", n);
}
// unwrap_or 提供默认值
let value = some_number.unwrap_or(0);
Result 类型
表示操作可能成功或失败。
// 定义
enum Result<T, E> {
Ok(T), // 成功
Err(E), // 失败
}
// 使用
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err(String::from("除数不能为0"))
} else {
Ok(a / b)
}
}
// 匹配处理
match divide(10.0, 2.0) {
Ok(result) => println!("结果: {}", result),
Err(err) => println!("错误: {}", err),
}
// ? 操作符(自动传播错误)
fn my_function() -> Result<(), String> {
let result = divide(10.0, 0.0)?; // 如果是 Err,提前返回
println!("结果: {}", result);
Ok(())
}
错误处理
? 操作符
// 展开前
fn read_file() -> Result<String, std::io::Error> {
let f = File::open("file.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut contents = String::new();
match f.read_to_string(&mut contents) {
Ok(_) => Ok(contents),
Err(e) => Err(e),
}
}
// 使用 ? 简化
fn read_file() -> Result<String, std::io::Error> {
let mut f = File::open("file.txt")?; // 如果失败,自动返回 Err
let mut contents = String::new();
f.read_to_string(&mut contents)?; // 同上
Ok(contents)
}
项目中的例子
// config/database.rs
pub async fn init_database(pool: &MySqlPool) -> Result<(), sqlx::Error> {
sqlx::query("CREATE TABLE...")
.execute(pool)
.await?; // 如果失败,返回错误
Ok(()) // 成功,返回空
}
// main.rs
database::init_database(&db).await?;
// 如果失败,main 函数返回错误,程序退出
异步编程
async/await 基础
// 异步函数
async fn fetch_data() -> String {
// 模拟异步操作
tokio::time::sleep(Duration::from_secs(1)).await;
String::from("data")
}
// 调用异步函数
#[tokio::main]
async fn main() {
let data = fetch_data().await; // .await 等待完成
println!("{}", data);
}
为什么需要异步?
// 同步代码(阻塞)
fn fetch_user() -> User {
// 等待数据库查询(阻塞线程)
let user = db.query("SELECT...").execute();
user
}
// 异步代码(非阻塞)
async fn fetch_user() -> User {
// 等待数据库查询(不阻塞线程,可以处理其他任务)
let user = db.query("SELECT...").execute().await;
user
}
项目中的例子
// services/user_service.rs
pub async fn create_user(&self, payload: CreateUserRequest) -> Result<User> {
// 异步数据库操作
let result = sqlx::query(...)
.execute(&self.db)
.await?; // await 等待数据库响应
let user_id = result.6917333 as i64;
// 异步查询
let user = sqlx::query_as::<_, User>(...)
.fetch_one(&self.db)
.await?; // 再次等待
Ok(user)
}
常用宏
println! / format!
// 打印
println!("Hello, world!");
println!("x = {}", x); // 格式化
println!("x = {:?}", complex_struct); // Debug 格式
// 字符串格式化
let s = format!("x = {}, y = {}", x, y);
vec!
// 创建向量(动态数组)
let v = vec![1, 2, 3, 4, 5];
derive
// 自动实现 trait
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
name: String,
age: u32,
}
// 等价于手动实现
impl Debug for User { ... }
impl Clone for User { ... }
impl Serialize for User { ... }
impl Deserialize for User { ... }
项目中的实例
1. 结构体和方法
// config/app_state.rs
// 定义结构体
#[derive(Clone)]
pub struct AppState {
pub db: MySqlPool, // 字段:数据库连接池
pub cache: RedisCache, // 字段:Redis 缓存
}
// 实现方法
impl AppState {
// 关联函数(类似构造函数)
pub fn new(db: MySqlPool, cache: RedisCache) -> Self {
Self { db, cache }
}
}
// 使用
let state = AppState::new(db, cache);
2. 所有权和借用
// config/database.rs
// 借用参数(不获取所有权)
pub async fn create_pool(database_url: &str) -> Result<MySqlPool> {
// ^^^^ 借用
MySqlPoolOptions::new()
.connect(database_url) // 使用借用的值
.await
}
// main.rs
let config = Config::from_env();
let db = database::create_pool(&config.database_url).await?;
// ^^^ 传递借用
// config 仍然有效,可以继续使用
3. Option 和模式匹配
// services/user_service.rs
pub async fn get_user(&self, user_id: i64) -> Result<Option<User>> {
// 查询可能返回 None(用户不存在)
let user = sqlx::query_as::<_, User>(...)
.fetch_optional(&self.db) // 返回 Option<User>
.await?;
// if let 匹配 Some
if let Some(ref u) = user {
// 用户存在,缓存它
let user_json = serde_json::to_string(u)?;
self.cache.set(...).await;
}
Ok(user) // 返回 Option<User>
}
// controllers/user_controller.rs
// 模式匹配
match user_service.get_user(user_id).await {
Ok(Some(user)) => {
// 找到用户
Ok((StatusCode::OK, Json(user.into())))
}
Ok(None) => {
// 用户不存在
Err((StatusCode::NOT_FOUND, Json(json!({"error": "User not found"}))))
}
Err(e) => {
// 数据库错误
Err((StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": e.to_string()}))))
}
}
4. Result 和 ? 操作符
// services/user_service.rs
pub async fn create_user(&self, payload: CreateUserRequest) -> Result<User> {
// ? 自动处理错误
let result = sqlx::query(...)
.execute(&self.db)
.await?; // 如果失败,提前返回 Err
let user_id = result.6917333 as i64;
let user = sqlx::query_as::<_, User>(...)
.fetch_one(&self.db)
.await?; // 再次使用 ?
Ok(user) // 成功,返回 Ok(user)
}
5. 异步编程
// main.rs
#[tokio::main] // 宏:创建异步运行时
async fn main() -> anyhow::Result<()> {
// 异步操作
let db = database::create_pool(...).await?;
// ^^^^^^ await 等待完成
let cache = RedisCache::new(...).await?;
// ^^^^^^ await 等待完成
// 启动服务器(永久运行)
axum::Server::bind(...)
.serve(...)
.await?; // await 等待服务器关闭
Ok(())
}
6. trait 和泛型
// models/user.rs
// From trait:类型转换
impl From<User> for UserResponse {
fn from(user: User) -> Self {
UserResponse {
id: user.id,
username: user.username,
email: user.email,
created_at: user.created_at,
updated_at: user.updated_at,
}
}
}
// 使用
let user: User = fetch_from_db();
let response: UserResponse = user.into(); // 调用 from
学习建议
-
从简单开始
- 先理解基本语法(变量、函数、结构体)
- 再学习所有权系统
- 最后学习异步编程
-
阅读代码顺序
- main.rs → 了解程序入口
- config/ → 了解配置管理
- models/ → 了解数据结构
- utils/ → 了解工具函数
- services/ → 了解业务逻辑
- controllers/ → 了解请求处理
- routes/ → 了解路由配置
-
动手实践
- 修改现有代码
- 添加新功能
- 观察编译器错误信息(Rust 编译器非常友好)
-
参考资源
- Rust 官方书(强烈推荐)
- Rust by Example
- Tokio 教程
常见问题
Q: 为什么有时用 & 有时不用?
A: & 表示借用,不获取所有权。使用场景:
- 函数参数通常用借用(除非需要所有权)
- 查看数据用借用
- 修改数据用可变借用
&mut - 转移所有权时不用
&
Q: Clone 的成本高吗?
A: 取决于类型:
- 简单类型(i32, f64等):Copy trait,成本低
- String: 完全复制,成本较高
- MySqlPool, RedisCache: 使用 Arc,clone 只增加引用计数,成本低
Q: async/await 和 sync 有什么区别?
A:
- 同步:线程等待操作完成(阻塞)
- 异步:线程不等待,可以处理其他任务(非阻塞)
- 异步更高效,特别是 I/O 密集型应用
Q: Option 和 Result 什么时候用?
A:
- Option:表示值可能不存在(类似 nullable)
- Result:表示操作可能失败(带错误信息)
祝你学习愉快!如有疑问,请参考注释或 Rust 官方文档。