Rust 结构体、枚举、模式匹配、Option 和 Result、包和模块
1. 结构体(Structs)深度解析
什么是结构体?
结构体是一种自定义数据类型,允许你将多个相关的值组合在一起,形成一个有意义的组合。
定义和使用结构体
// 1. 基本结构体定义
struct User {
username: String, // 字段:用户名
email: String, // 字段:邮箱
sign_in_count: u64, // 字段:登录次数
active: bool, // 字段:是否活跃
}
// 2. 创建结构体实例
fn create_user() {
// 不可变实例
let user1 = User {
email: String::from("alice@example.com"),
username: String::from("alice123"),
active: true,
sign_in_count: 1,
};
// 可变实例
let mut user2 = User {
email: String::from("bob@example.com"),
username: String::from("bob456"),
active: true,
sign_in_count: 1,
};
// 修改可变实例的字段
user2.sign_in_count = 2;
user2.email = String::from("bob.new@example.com");
println!("用户1邮箱: {}", user1.email);
println!("用户2登录次数: {}", user2.sign_in_count);
}
字段初始化简写
fn build_user(email: String, username: String) -> User {
// 当参数名与字段名相同时,可以简写
User {
email, // 等同于 email: email
username, // 等同于 username: username
active: true,
sign_in_count: 1,
}
}
结构体更新语法
fn update_user() {
let user1 = User {
email: String::from("user1@example.com"),
username: String::from("user1"),
active: true,
sign_in_count: 5,
};
// 使用结构体更新语法创建新实例
let user2 = User {
email: String::from("user2@example.com"),
username: String::from("user2"),
..user1 // 其余字段使用 user1 的值
};
// user2 的 active 和 sign_in_count 与 user1 相同
println!("user2活跃状态: {}", user2.active); // true
println!("user2登录次数: {}", user2.sign_in_count); // 5
}
元组结构体
// 元组结构体 - 有名字的元组
struct Color(i32, i32, i32); // RGB颜色
struct Point(i32, i32, i32); // 3D坐标
fn use_tuple_structs() {
let black = Color(0, 0, 0);
let white = Color(255, 255, 255);
let origin = Point(0, 0, 0);
// 通过索引访问
println!("黑色 R: {}, G: {}, B: {}", black.0, black.1, black.2);
println!("原点坐标: ({}, {}, {})", origin.0, origin.1, origin.2);
// 模式匹配解构
let Color(r, g, b) = white;
println!("白色 RGB: {}, {}, {}", r, g, b);
}
类单元结构体
// 没有任何字段的结构体,用于需要在类型上实现trait但不需要数据的情况
struct Marker;
struct ClickEvent;
fn use_unit_structs() {
let marker = Marker;
let click = ClickEvent;
// 这些结构体不包含数据,只表示类型
}
结构体方法详解
// 为 User 结构体实现方法
impl User {
// 关联函数(类似静态方法) - 没有 self 参数
fn new(username: String, email: String) -> User {
User {
username,
email,
active: true,
sign_in_count: 0,
}
}
// 不可变引用方法 - &self
fn get_info(&self) -> String {
format!("用户: {} <{}>", self.username, self.email)
}
// 检查用户是否活跃
fn is_active(&self) -> bool {
self.active
}
// 可变引用方法 - &mut self
fn update_email(&mut self, new_email: String) {
self.email = new_email;
self.sign_in_count += 1; // 每次更新都算一次活动
}
// 获取所有权的方法 - self
fn deactivate(self) -> String {
format!("用户 {} 已被停用", self.username)
// 这里 self 被消耗,之后不能再使用
}
}
// 可以有多个 impl 块
impl User {
fn increment_sign_in(&mut self) {
self.sign_in_count += 1;
}
}
fn use_methods() {
let mut user = User::new(
String::from("charlie"),
String::from("charlie@example.com")
);
// 调用方法
println!("{}", user.get_info());
println!("是否活跃: {}", user.is_active());
user.update_email(String::from("charlie.new@example.com"));
user.increment_sign_in();
let message = user.deactivate(); // user 的所有权被移动
println!("{}", message);
// 这里不能再使用 user
}
2. 枚举(Enums)深度解析
基本枚举
// 简单枚举
enum Direction {
North,
South,
East,
West,
}
// 带数据的枚举
enum WebEvent {
PageLoad, // 单元变体
KeyPress(char), // 元组变体
Paste(String), // 元组变体
Click { x: i64, y: i64 }, // 结构体变体
}
枚举的使用
fn use_enums() {
// 创建枚举实例
let direction = Direction::North;
let event1 = WebEvent::PageLoad;
let event2 = WebEvent::KeyPress('a');
let event3 = WebEvent::Paste(String::from("hello"));
let event4 = WebEvent::Click { x: 100, y: 200 };
// 处理枚举
match direction {
Direction::North => println!("向北"),
Direction::South => println!("向南"),
Direction::East => println!("向东"),
Direction::West => println!("向西"),
}
}
枚举方法
impl WebEvent {
fn description(&self) -> String {
match self {
WebEvent::PageLoad => String::from("页面加载"),
WebEvent::KeyPress(c) => format!("按键: {}", c),
WebEvent::Paste(s) => format!("粘贴: {}", s),
WebEvent::Click { x, y } => format!("点击位置: ({}, {})", x, y),
}
}
fn is_user_action(&self) -> bool {
match self {
WebEvent::PageLoad => false,
_ => true, // 其他都是用户操作
}
}
}
fn use_enum_methods() {
let events = [
WebEvent::PageLoad,
WebEvent::KeyPress('x'),
WebEvent::Click { x: 50, y: 75 },
];
for event in events.iter() {
println!("事件: {}, 用户操作: {}",
event.description(),
event.is_user_action());
}
}
3. 模式匹配(Pattern Matching)深度解析
match 表达式
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState), // 携带状态的25美分
}
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
California,
// ... 其他州
}
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
}
}
}
fn comprehensive_matching() {
let coin = Coin::Quarter(UsState::California);
let value = value_in_cents(coin);
println!("硬币价值: {} 美分", value);
}
匹配 Option
fn handle_option() {
let some_number = Some(5);
let no_number: Option<i32> = None;
// 匹配 Some 和 None
match some_number {
Some(i) => println!("有值: {}", i),
None => println!("没有值"),
}
match no_number {
Some(i) => println!("有值: {}", i),
None => println!("没有值"),
}
// 更复杂的匹配
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
let six = plus_one(Some(5));
let none = plus_one(None);
println!("plus_one(Some(5)) = {:?}", six); // Some(6)
println!("plus_one(None) = {:?}", none); // None
}
通配模式和 _ 占位符
fn catch_all_patterns() {
let dice_roll = 9;
match dice_roll {
3 => println!("移动3格"),
7 => println!("幸运7,再掷一次"),
other => println!("移动 {} 格", other), // 捕获所有其他值
}
// 使用 _ 忽略值
match dice_roll {
1 => println!("蛇梯棋,上梯子"),
6 => println!("蛇梯棋,下梯子"),
_ => (), // 什么都不做
}
// 匹配范围
let age = 25;
match age {
0..=12 => println!("儿童"),
13..=19 => println!("青少年"),
20..=64 => println!("成人"),
_ => println!("长者"),
}
}
if let 简洁控制流
fn if_let_usage() {
let config_max = Some(3u8);
// 使用 match
match config_max {
Some(max) => println!("最大配置: {}", max),
_ => (),
}
// 使用 if let - 更简洁
if let Some(max) = config_max {
println!("最大配置: {}", max);
}
// if let 与 else 配合
let mut count = 0;
let coin = Coin::Quarter(UsState::Alaska);
if let Coin::Quarter(state) = coin {
println!("州的25美分来自 {:?}", state);
} else {
count += 1;
}
// 多个模式匹配
let number = 13;
if let 1 | 2 | 3 | 5 | 7 | 11 | 13 = number {
println!("{} 是质数", number);
}
}
4. Option 和 Result 深度解析
Option 详解
fn option_deep_dive() {
// Option 的定义(标准库中)
// enum Option<T> {
// Some(T),
// None,
// }
// 创建 Option
let some_number: Option<i32> = Some(5);
let some_string: Option<&str> = Some("hello");
let absent_number: Option<i32> = None;
// 使用 match 处理 Option
fn square(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i * i),
}
}
println!("square(Some(5)) = {:?}", square(Some(5))); // Some(25)
println!("square(None) = {:?}", square(None)); // None
// Option 的常用方法
let x = Some(10);
// unwrap - 获取 Some 中的值,如果是 None 则 panic
let value = x.unwrap(); // 10
// let bad = absent_number.unwrap(); // 这会 panic!
// unwrap_or - 提供默认值
let value_or_default = absent_number.unwrap_or(0); // 0
// unwrap_or_else - 通过闭包计算默认值
let value_or_computed = absent_number.unwrap_or_else(|| {
println!("计算默认值");
42
}); // 42
// map - 转换 Some 中的值
let doubled = x.map(|n| n * 2); // Some(20)
let still_none = absent_number.map(|n| n * 2); // None
// and_then - 链式操作
fn parse_number(s: &str) -> Option<i32> {
s.parse().ok() // 将 Result 转换为 Option
}
let result = Some("42").and_then(parse_number); // Some(42)
let bad_result = Some("hello").and_then(parse_number); // None
// 组合使用
let complex = Some("123")
.and_then(parse_number) // Some(123)
.map(|n| n * 2) // Some(246)
.unwrap_or(0); // 246
println!("复杂操作结果: {}", complex);
}
Result<T, E> 详解
use std::fs::File;
use std::io::{self, Read, ErrorKind};
fn result_deep_dive() {
// Result 的定义
// enum Result<T, E> {
// Ok(T),
// Err(E),
// }
// 基本使用
let file_result = File::open("hello.txt");
// 使用 match 处理 Result
let file = match file_result {
Ok(file) => file,
Err(error) => {
panic!("打开文件失败: {:?}", error);
}
};
// 更细致的错误处理
let detailed_result = File::open("hello.txt");
let file = match detailed_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => {
// 文件不存在,尝试创建
match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("创建文件失败: {:?}", e),
}
}
other_error => {
panic!("打开文件失败: {:?}", other_error);
}
},
};
}
? 运算符
// 传播错误的传统方式
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_simple() -> Result<String, io::Error> {
let mut f = File::open("hello.txt")?; // 如果出错,立即返回错误
let mut s = String::new();
f.read_to_string(&mut s)?; // 同样,出错立即返回
Ok(s)
}
// 更简洁的写法
fn read_username_from_file_shorter() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
// 使用 std::fs 的便捷函数
fn read_username_easiest() -> Result<String, io::Error> {
std::fs::read_to_string("hello.txt")
}
// 在 main 函数中使用 Result
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = std::fs::read_to_string("hello.txt")?;
println!("文件内容: {}", content);
Ok(()) // 成功返回
}
组合 Option 和 Result
fn combine_option_result() {
// Option 转 Result
let some_number = Some(42);
let result: Result<i32, &str> = some_number.ok_or("没有值");
// Result 转 Option
let good_result: Result<i32, &str> = Ok(42);
let good_option = good_result.ok(); // Some(42)
let bad_result: Result<i32, &str> = Err("错误");
let bad_option = bad_result.ok(); // None
// 复杂组合
fn process_input(input: &str) -> Option<i32> {
input.parse::<i32>().ok().filter(|&n| n > 0)
}
println!("process_input(\"42\") = {:?}", process_input("42")); // Some(42)
println!("process_input(\"-5\") = {:?}", process_input("-5")); // None
println!("process_input(\"abc\") = {:?}", process_input("abc")); // None
}
5. 包和模块深度解析
项目结构示例
my_project/
├── Cargo.toml # 包配置
├── Cargo.lock # 依赖锁文件
└── src/
├── main.rs # 二进制crate根
├── lib.rs # 库crate根
├── utils/ # 工具模块
│ ├── mod.rs # utils模块声明
│ └── helpers.rs # 辅助函数
├── models/ # 数据模型
│ ├── mod.rs
│ └── user.rs
└── api/ # API模块
├── mod.rs
└── v1.rs
模块系统详解
// src/lib.rs
// 声明模块
pub mod utils; // 从 utils/mod.rs 或 utils.rs 加载
pub mod models;
pub mod api;
// 使用模块中的项
use crate::utils::helpers::some_helper_function;
use models::user::User;
// 重新导出(pub use)
pub use api::v1::make_api_request;
// 主库函数
pub fn library_function() -> String {
some_helper_function()
}
// src/utils/mod.rs
// 声明子模块
pub mod helpers;
// 工具模块的公共函数
pub fn setup() {
println!("工具模块初始化");
}
// 私有函数(只能在模块内部使用)
fn internal_helper() {
println!("内部辅助函数");
}
// src/utils/helpers.rs
// 公共函数
pub fn some_helper_function() -> String {
String::from("帮助函数被调用")
}
// 只能在当前crate内访问的函数
pub(crate) fn crate_private_function() {
println!("只能在当前crate内访问");
}
// 模块私有函数
fn private_function() {
println!("这个函数只在 helpers 模块内可见");
}
// src/models/mod.rs
pub mod user;
// 模型相关的公共接口
pub fn initialize_models() {
println!("模型初始化");
}
// src/models/user.rs
use std::fmt;
// 公共结构体
#[derive(Debug)]
pub struct User {
pub username: String,
pub email: String,
age: u8, // 私有字段
}
impl User {
// 公共构造函数
pub fn new(username: String, email: String, age: u8) -> User {
User { username, email, age }
}
// 公共方法
pub fn get_age(&self) -> u8 {
self.age
}
// 私有方法
fn validate(&self) -> bool {
!self.username.is_empty() && !self.email.is_empty()
}
}
// 实现 Display trait
impl fmt::Display for User {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "User: {} <{}>", self.username, self.email)
}
}
// src/api/mod.rs
pub mod v1;
pub fn initialize_api() {
println!("API模块初始化");
}
// src/api/v1.rs
// API v1 端点
pub fn make_api_request(endpoint: &str) -> String {
format!("调用API v1 端点: {}", endpoint)
}
pub fn get_user_data(user_id: u32) -> String {
format!("获取用户 {} 的数据", user_id)
}
使用声明详解
// src/main.rs
// 引入库crate
use my_project::{
utils,
models::user::User,
make_api_request
};
// 标准库引入
use std::collections::{HashMap, HashSet};
use std::io::{self, Write};
// 重命名引入
use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;
fn main() {
// 使用库函数
let result = my_project::library_function();
println!("{}", result);
// 使用引入的模块
utils::setup();
// 创建用户实例
let user = User::new(
String::from("alice"),
String::from("alice@example.com"),
25
);
println!("{}", user);
println!("用户年龄: {}", user.get_age());
// 使用重新导出的函数
let api_response = make_api_request("/users");
println!("{}", api_response);
// 使用通配符引入(谨慎使用)
use std::collections::*;
let mut map = HashMap::new();
map.insert("key", "value");
let mut set = HashSet::new();
set.insert(1);
}
可见性规则
// 可见性修饰符示例
mod visibility_example {
// 公共项 - 任何地方都可访问
pub struct PublicStruct {
pub public_field: i32,
private_field: i32, // 即使结构体是公共的,字段也可以是私有的
}
// 结构体实现
impl PublicStruct {
pub fn new() -> PublicStruct {
PublicStruct {
public_field: 1,
private_field: 2,
}
}
// 公共方法可以访问私有字段
pub fn get_private(&self) -> i32 {
self.private_field
}
// 私有方法
fn internal_method(&self) {
println!("内部方法");
}
}
// 只在当前crate内可见
pub(crate) struct CratePublicStruct {
pub value: i32,
}
// 只在父模块内可见
pub(super) struct SuperPublicStruct;
// 私有项(默认)
struct PrivateStruct;
// 公共枚举的所有变体默认都是公共的
pub enum PublicEnum {
Variant1,
Variant2,
}
}
高级模块特性
// 内联模块
mod inline_module {
pub fn public_function() {
println!("内联模块的公共函数");
}
fn private_function() {
println!("内联模块的私有函数");
}
// 嵌套模块
pub mod nested {
pub fn function() {
println!("嵌套模块函数");
}
}
}
// 使用 cfg 条件编译
#[cfg(target_os = "linux")]
mod linux_specific {
pub fn linux_only_function() {
println!("这是Linux特有的功能");
}
}
#[cfg(target_os = "windows")]
mod windows_specific {
pub fn windows_only_function() {
println!("这是Windows特有的功能");
}
}
// 测试模块
#[cfg(test)]
mod tests {
use super::*; // 引入父模块的所有项
#[test]
fn test_user_creation() {
let user = User::new("test".to_string(), "test@example.com".to_string(), 30);
assert_eq!(user.get_age(), 30);
assert_eq!(user.username, "test");
}
}
综合实战示例
// 一个完整的博客系统示例
use std::error::Error;
use std::fmt;
// 错误类型
#[derive(Debug)]
pub enum BlogError {
InvalidTitle,
InvalidContent,
PostNotFound,
}
impl fmt::Display for BlogError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BlogError::InvalidTitle => write!(f, "标题无效"),
BlogError::InvalidContent => write!(f, "内容无效"),
BlogError::PostNotFound => write!(f, "文章未找到"),
}
}
}
impl Error for BlogError {}
// 文章状态
#[derive(Debug, Clone, PartialEq)]
pub enum PostStatus {
Draft,
Published,
Archived,
}
// 文章结构体
pub struct BlogPost {
title: String,
content: String,
author: String,
status: PostStatus,
created_at: String, // 简化处理,实际应该用 DateTime
}
impl BlogPost {
pub fn new(title: String, content: String, author: String) -> Result<Self, BlogError> {
if title.trim().is_empty() {
return Err(BlogError::InvalidTitle);
}
if content.trim().is_empty() {
return Err(BlogError::InvalidContent);
}
Ok(BlogPost {
title,
content,
author,
status: PostStatus::Draft,
created_at: "2024-01-01".to_string(), // 简化
})
}
pub fn publish(&mut self) {
self.status = PostStatus::Published;
}
pub fn archive(&mut self) {
self.status = PostStatus::Archived;
}
// 获取器方法
pub fn title(&self) -> &str {
&self.title
}
pub fn content(&self) -> &str {
&self.content
}
pub fn author(&self) -> &str {
&self.author
}
pub fn status(&self) -> &PostStatus {
&self.status
}
pub fn preview(&self) -> String {
if self.content.len() > 100 {
format!("{}...", &self.content[..100])
} else {
self.content.clone()
}
}
}
// 博客系统
pub struct Blog {
posts: Vec<BlogPost>,
name: String,
}
impl Blog {
pub fn new(name: String) -> Self {
Blog {
posts: Vec::new(),
name,
}
}
pub fn add_post(&mut self, post: BlogPost) {
self.posts.push(post);
}
pub fn get_post(&self, index: usize) -> Option<&BlogPost> {
self.posts.get(index)
}
pub fn get_published_posts(&self) -> Vec<&BlogPost> {
self.posts
.iter()
.filter(|post| post.status() == &PostStatus::Published)
.collect()
}
pub fn publish_post(&mut self, index: usize) -> Result<(), BlogError> {
match self.posts.get_mut(index) {
Some(post) => {
post.publish();
Ok(())
}
None => Err(BlogError::PostNotFound),
}
}
}
// 使用示例
fn main() -> Result<(), Box<dyn Error>> {
let mut my_blog = Blog::new("我的技术博客".to_string());
// 创建文章
let post1 = BlogPost::new(
"学习Rust".to_string(),
"Rust是一门很棒的系统编程语言...".to_string(),
"张三".to_string()
)?;
let post2 = BlogPost::new(
"模式匹配详解".to_string(),
"模式匹配是Rust中非常强大的特性...".to_string(),
"李四".to_string()
)?;
// 添加文章到博客
my_blog.add_post(post1);
my_blog.add_post(post2);
// 发布第一篇文章
my_blog.publish_post(0)?;
// 显示已发布的文章
println!("博客: {}", my_blog.name);
println!("已发布文章:");
for post in my_blog.get_published_posts() {
println!("标题: {}", post.title());
println!("作者: {}", post.author());
println!("预览: {}", post.preview());
println!("状态: {:?}", post.status());
println!("---");
}
Ok(())
}
贵公网安备52052402000220号