Rust 复合类型高级用法——结构体、枚举与模式匹配进阶
Rust 复合类型高级用法涵盖结构体、枚举与模式匹配的进阶技巧。文章讲解泛型结构体与枚举的定义,实现类型安全复用。介绍结构体字段验证方法及派生宏的高级配置。深入探讨枚举的嵌套模式匹配与判别式处理。详细阐述 trait 关联类型、默认实现及对象安全的动态分发机制。通过通用缓存系统与 HTTP 请求解析器两个实战案例,展示复合类型在真实场景中的应用,并提供常见问题解决方案。

Rust 复合类型高级用法涵盖结构体、枚举与模式匹配的进阶技巧。文章讲解泛型结构体与枚举的定义,实现类型安全复用。介绍结构体字段验证方法及派生宏的高级配置。深入探讨枚举的嵌套模式匹配与判别式处理。详细阐述 trait 关联类型、默认实现及对象安全的动态分发机制。通过通用缓存系统与 HTTP 请求解析器两个实战案例,展示复合类型在真实场景中的应用,并提供常见问题解决方案。

💡 三大核心难点:
⚠️ 三大高频错误点:
泛型结构体允许我们在定义时使用占位类型参数,在实例化时指定具体类型,实现类型安全的代码复用。
⌨️ 泛型结构体基本示例:
// 定义一个泛型的 Option 类(类似标准库的 Option<T>)
#[derive(Debug, PartialEq)]
enum MyOption<T> {
Some(T),
None,
}
// 定义一个泛型的栈数据结构
#[derive(Debug)]
struct Stack<T> {
data: Vec<T>,
}
impl<T> Stack<T> {
// 创建新的空栈
fn new() -> Self {
Stack {
data: Vec::new(),
}
}
// 入栈操作
fn push(&mut self, value: T) {
self.data.push(value);
}
// 出栈操作
fn pop(&mut self) -> MyOption<T> {
if let Some(value) = self.data.pop() {
MyOption::Some(value)
} else {
MyOption::None
}
}
// 获取栈顶元素(不弹出)
fn peek(&self) -> MyOption<&T> {
self.data.last().map(|value| value)
}
}
fn main() {
// 实例化存储 i32 类型的栈
let mut int_stack = Stack::new();
int_stack.push(10);
int_stack.push(20);
int_stack.push(30);
println!("int_stack: {:?}", int_stack);
println!("peek: {:?}", int_stack.peek()); // MyOption::Some(30)
println!("pop: {:?}", int_stack.pop()); // MyOption::Some(30)
// 实例化存储 String 类型的栈
let mut str_stack = Stack::new();
str_stack.push(String::from("Hello"));
str_stack.push(String::from("Rust"));
println!("str_stack: {:?}", str_stack);
println!("pop: {:?}", str_stack.pop()); // MyOption::Some("Rust")
}
在 Rust 中,我们可以通过自定义 new 方法或实现 TryFrom trait来验证结构体字段的有效性,确保实例化后的对象满足业务规则。
⌨️ 结构体字段验证示例:
use std::convert::TryFrom;
use std::error::Error;
use std::fmt;
// 定义自定义错误类型
#[derive(Debug)]
struct ValidationError {
field: String,
message: String,
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.field, self.message)
}
}
impl Error for ValidationError {}
// 定义用户结构体,包含字段验证逻辑
#[derive(Debug, PartialEq)]
struct User {
id: u32,
name: String,
email: String,
age: u8,
}
impl User {
// 自定义 new 方法,返回 Result 类型
fn new(id: u32, name: String, email: String, age: u8) -> Result<Self, ValidationError> {
// 验证 id 是否大于 0
if id == 0 {
return Err(ValidationError {
field: "id".to_string(),
message: "ID 必须大于 0".to_string(),
});
}
// 验证 name 长度是否在 3-20 个字符之间
if name.len() < 3 || name.len() > 20 {
return Err(ValidationError {
field: "name".to_string(),
message: "姓名长度必须在 3-20 个字符之间".to_string(),
});
}
// 验证 email 是否包含@符号
if !email.contains('@') {
return Err(ValidationError {
field: "email".to_string(),
message: "邮箱地址必须包含@符号".to_string(),
});
}
// 验证 age 是否在 18-120 岁之间
if age < 18 || age > 120 {
return Err(ValidationError {
field: "age".to_string(),
message: "年龄必须在 18-120 岁之间".to_string(),
});
}
Ok(User { id, name, email, age })
}
}
// 实现 TryFrom trait,从元组转换为 User
impl TryFrom<(u32, String, String, u8)> for User {
type Error = ValidationError;
fn try_from(value: (u32, String, String, u8)) -> Result<Self, Self::Error> {
Self::new(value.0, value.1, value.2, value.3)
}
}
fn main() {
// 正确的用户信息
let valid_user = User::new(1, "张三".to_string(), "[email protected]".to_string(), 25);
if let Ok(user) = valid_user {
println!("正确的用户信息:{:?}", user);
}
// 错误的用户信息(姓名长度不足)
let invalid_user1 = User::new(2, "张".to_string(), "[email protected]".to_string(), 30);
if let Err(e) = invalid_user1 {
println!("错误的用户信息 1: {:?}", e);
}
// 使用 TryFrom trait 转换
let user_tuple = (3, "李四".to_string(), "[email protected]".to_string(), 28);
let user_from_tuple = User::try_from(user_tuple);
if let Ok(user) = user_from_tuple {
println!("从元组转换的用户信息:{:?}", user);
}
}
Rust 标准库提供了10+ 个内置派生宏(如 Debug、PartialEq、Clone、Copy、Hash 等),我们可以通过属性配置来优化它们的行为,或者使用**第三方库(如 derive_builder)**来生成更复杂的代码。
⌨️ 结构体派生宏高级应用示例:
use derive_builder::Builder;
// 需要在 Cargo.toml 中添加依赖:derive_builder = "0.12"
// 使用 Debug 的属性配置来美化输出
#[derive(Debug, PartialEq)]
#[debug(skip(field_to_skip))]
struct DebugExample {
field1: i32,
field2: String,
field_to_skip: Vec<u8>,
}
// 使用 derive_builder 宏生成建造者模式的代码
#[derive(Debug, Builder)]
#[builder(pattern = "owned", setter(into, strip_option))]
struct Product {
name: String,
price: f64,
description: Option<String>,
stock: u32,
}
impl Product {
fn new() -> ProductBuilder {
ProductBuilder::default()
}
}
fn main() {
// Debug 属性配置示例
let debug_example = DebugExample {
field1: 10,
field2: "Hello, Rust!".to_string(),
field_to_skip: vec![1, 2, 3],
};
println!("DebugExample: {:?}", debug_example);
// 建造者模式示例
let product = Product::new()
.name("Rust Programming Book")
.price(99.0)
.description("A comprehensive guide to Rust development")
.stock(100)
.build()
.expect("Failed to build product");
println!("Product: {:?}", product);
}
带泛型参数的枚举允许我们定义灵活的类型变体,例如标准库的 Result<T, E> 枚举就带了两个泛型参数,分别表示成功值和错误值。
⌨️ 带泛型参数的枚举示例:
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
// 自定义文件操作错误类型
#[derive(Debug)]
enum FileError {
NotFound(PathBuf),
PermissionDenied(PathBuf),
ReadError(PathBuf, String),
}
impl fmt::Display for FileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FileError::NotFound(path) => write!(f, "文件未找到:{}", path.display()),
FileError::PermissionDenied(path) => write!(f, "权限不足:{}", path.display()),
FileError::ReadError(path, msg) => write!(f, "读取文件失败:{} ({})", path.display(), msg),
}
}
}
impl Error for FileError {}
// 带泛型参数的文件操作结果类型
type FileResult<T> = Result<T, FileError>;
// 读取文件内容的函数
fn read_file_content(path: PathBuf) -> FileResult<String> {
// 尝试打开文件
let mut file = match File::open(&path) {
Ok(file) => file,
Err(e) => match e.kind() {
std::io::ErrorKind::NotFound => return Err(FileError::NotFound(path)),
std::io::ErrorKind::PermissionDenied => return Err(FileError::PermissionDenied(path)),
_ => return Err(FileError::ReadError(path, e.to_string())),
},
};
// 尝试读取文件内容
let mut content = String::new();
match file.read_to_string(&mut content) {
Ok(_) => Ok(content),
Err(e) => Err(FileError::ReadError(path, e.to_string())),
}
}
fn main() {
// 读取存在的文件
let existing_file = PathBuf::from("src/main.rs");
match read_file_content(existing_file) {
Ok(content) => println!("文件内容:{}", content),
Err(e) => println!("错误:{}", e),
}
// 读取不存在的文件
let non_existent_file = PathBuf::from("nonexistent.txt");
match read_file_content(non_existent_file) {
Ok(content) => println!("文件内容:{}", content),
Err(e) => println!("错误:{}", e),
}
}
对于嵌套的枚举/结构体,我们可以使用嵌套模式匹配来简化代码,直接访问内部字段的值。
⌨️ 枚举的嵌套模式匹配示例:
#[derive(Debug)]
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor { red: u8, green: u8, blue: u8 },
SendData { id: u32, payload: Payload },
}
#[derive(Debug)]
enum Payload {
Text(String),
Binary(Vec<u8>),
}
fn process_message(msg: Message) {
match msg {
// 简单匹配
Message::Quit => println!("收到退出消息"),
// 匹配结构体字段
Message::Move { x, y } if x > 0 && y > 0 => println!("收到向右上方移动的消息:({}, {})", x, y),
Message::Move { x, y } => println!("收到移动消息:({}, {})", x, y),
// 匹配枚举字段
Message::Write(text) => println!("收到写入消息:{}", text),
// 匹配深层嵌套的字段
Message::SendData { id, payload: Payload::Text(text) } => println!("收到文本数据:ID = {}, 内容 = {}", id, text),
Message::SendData { id, payload: Payload::Binary(data) } => println!("收到二进制数据:ID = {}, 长度 = {}", id, data.len()),
// 其他情况
_ => println!("收到未知消息"),
}
}
fn main() {
// 发送各种类型的消息
process_message(Message::Quit);
process_message(Message::Move { x: 10, y: 5 });
process_message(Message::Write("Hello, Rust!".to_string()));
process_message(Message::ChangeColor { red: 255, green: 0, blue: 0 });
process_message(Message::SendData { id: 1, payload: Payload::Text("This is a test message".to_string()) });
process_message(Message::SendData { id: 2, payload: Payload::Binary(vec![0x48, 0x65, 0x6C, 0x6C, 0x6F]) });
}
Rust 的枚举会自动为每个变体分配一个判别式(默认是 0、1、2…),我们可以通过 std::mem::discriminant 函数来获取判别式的值,或者通过属性配置来指定自定义的判别式。
⌨️ 枚举的判别式示例:
use std::mem;
// 定义一个带自定义判别式的枚举
#[repr(u8)] // 表示判别式的类型是 u8
enum StatusCode {
Ok = 200,
BadRequest = 400,
Unauthorized = 401,
Forbidden = 403,
NotFound = 404,
InternalServerError = 500,
}
// 定义一个默认判别式的枚举
enum Message {
Quit,
Move,
Write,
ChangeColor,
}
fn main() {
// 获取带自定义判别式的枚举变体的判别式
let ok_code = StatusCode::Ok;
let ok_discriminant = mem::discriminant(&ok_code);
let bad_request_code = StatusCode::BadRequest;
let bad_request_discriminant = mem::discriminant(&bad_request_code);
println!("StatusCode::Ok 的判别式:{:?}", ok_discriminant);
println!("StatusCode::BadRequest 的判别式:{:?}", bad_request_discriminant);
println!("StatusCode::Ok == StatusCode::BadRequest? {}", ok_discriminant == bad_request_discriminant);
// 获取默认判别式的枚举变体的判别式
let quit_msg = Message::Quit;
let quit_discriminant = mem::discriminant(&quit_msg);
let move_msg = Message::Move;
let move_discriminant = mem::discriminant(&move_msg);
println!("Message::Quit 的判别式:{:?}", quit_discriminant);
println!("Message::Move 的判别式:{:?}", move_discriminant);
println!("Message::Quit == Message::Move? {}", quit_discriminant == move_discriminant);
// 将判别式转换为原始整数类型
let ok_code_int = ok_code as u8;
println!("StatusCode::Ok 转换为 u8: {}", ok_code_int);
}
关联类型是 trait 中的占位类型,它允许我们在 trait 内部定义类型,而不需要在使用 trait 时指定泛型参数。关联类型更适合描述**'类型家族'**关系,例如迭代器的 Item 类型就是一个关联类型。
⌨️ 关联类型示例:
// 定义一个 Animal trait,包含关联类型 Sound
trait Animal {
type Sound; // 发出声音的方法
fn make_sound(&self) -> Self::Sound;
// 获取名字的方法
fn get_name(&self) -> String;
}
// 实现 Dog 类型的 Animal trait
struct Dog {
name: String,
}
impl Animal for Dog {
type Sound = String;
fn make_sound(&self) -> Self::Sound {
"Woof!".to_string()
}
fn get_name(&self) -> String {
self.name.clone()
}
}
// 实现 Cat 类型的 Animal trait
struct Cat {
name: String,
}
impl Animal for Cat {
type Sound = &'static str;
fn make_sound(&self) -> Self::Sound {
"Meow!"
}
fn get_name(&self) -> String {
self.name.clone()
}
}
// 使用关联类型的函数
fn print_animal_info<T: Animal>(animal: T) {
println!("{} says: {}", animal.get_name(), animal.make_sound());
}
fn main() {
let dog = Dog { name: "Buddy".to_string() };
let cat = Cat { name: "Misty".to_string() };
print_animal_info(dog); // 输出:Buddy says: Woof!
print_animal_info(cat); // 输出:Misty says: Meow!
}
我们可以在 trait 中为方法提供默认实现,如果某个类型实现了该 trait,但没有重新定义该方法,就会使用默认实现。
⌨️ trait 的默认实现示例:
// 定义一个 Shape trait,包含计算面积和周长的方法,周长有默认实现
trait Shape {
// 计算面积的方法(必须实现)
fn area(&self) -> f64;
// 计算周长的方法(有默认实现)
fn perimeter(&self) -> f64 {
0.0
}
// 打印形状信息的方法(有默认实现)
fn print_info(&self) {
println!("面积:{:.2}", self.area());
println!("周长:{:.2}", self.perimeter());
}
}
// 实现 Rectangle 类型的 Shape trait
struct Rectangle {
width: f64,
height: f64,
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
}
// 实现 Circle 类型的 Shape trait
struct Circle {
radius: f64,
}
impl Shape for Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
fn perimeter(&self) -> f64 {
2.0 * std::f64::consts::PI * self.radius
}
}
// 实现 Triangle 类型的 Shape trait(使用默认的 print_info 方法)
struct Triangle {
a: f64,
b: f64,
c: f64,
}
impl Shape for Triangle {
fn area(&self) -> f64 {
let s = (self.a + self.b + self.c) / 2.0;
(s * (s - self.a) * (s - self.b) * (s - self.c)).sqrt()
}
}
fn main() {
let rect = Rectangle { width: 10.0, height: 5.0 };
println!("矩形信息:");
rect.print_info();
println!("----------");
let circle = Circle { radius: 5.0 };
println!("圆形信息:");
circle.print_info();
println!("----------");
let triangle = Triangle { a: 3.0, b: 4.0, c: 5.0 };
println!("三角形信息:");
triangle.print_info();
}
trait 对象允许我们在运行时动态地确定类型,实现动态分发的功能。trait 对象的类型是 &dyn Trait(不可变引用)或 &mut dyn Trait(可变引用)或 Box<dyn Trait>(堆内存上的 trait 对象)。
⌨️ trait 对象示例:
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
// 定义一个 Writer trait,包含写入方法
trait Writer {
fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>>;
}
// 实现 FileWriter 类型的 Writer trait
struct FileWriter {
file: File,
}
impl FileWriter {
fn new(path: PathBuf) -> Result<Self, Box<dyn Error>> {
Ok(FileWriter { file: File::create(path)? })
}
}
impl Writer for FileWriter {
fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>> {
self.file.write_all(data)?;
self.file.flush()?;
Ok(())
}
}
// 实现 ConsoleWriter 类型的 Writer trait
struct ConsoleWriter;
impl Writer for ConsoleWriter {
fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>> {
let text = String::from_utf8(data.to_vec())?;
println!("{}", text);
Ok(())
}
}
// 使用 trait 对象的函数
fn write_data(writer: &mut dyn Writer, data: &[u8]) -> Result<(), Box<dyn Error>> {
writer.write(data)?;
Ok(())
}
fn main() {
// 使用 FileWriter 写入数据到文件
let mut file_writer = FileWriter::new(PathBuf::from("output.txt")).expect("Failed to create file writer");
write_data(&mut file_writer, b"Hello, Rust! (from file writer)").expect("Failed to write data to file");
// 使用 ConsoleWriter 写入数据到控制台
let mut console_writer = ConsoleWriter;
write_data(&mut console_writer, b"Hello, Rust! (from console writer)").expect("Failed to write data to console");
}
⚠️ trait 对象的对象安全问题: 一个 trait 要成为 trait 对象,必须满足对象安全的条件:
Sized trait(或者必须标记为 ?Sized)💡 场景分析:需要编写一个通用的数据缓存系统,支持不同类型的缓存策略(如 LRU、FIFO)和存储介质(如内存、Redis)。
⌨️ 代码示例:
use std::collections::HashMap;
use std::time::Duration;
// 定义缓存策略的 trait
trait CacheStrategy<K, V> {
// 插入数据时的策略方法
fn on_insert(&mut self, key: K, value: V);
// 访问数据时的策略方法
fn on_access(&mut self, key: &K);
// 删除数据时的策略方法(返回需要删除的 key)
fn on_delete(&mut self) -> Option<K>;
// 获取缓存的大小
fn len(&self) -> usize;
// 获取缓存的容量
fn capacity(&self) -> usize;
// 判断缓存是否已满
fn is_full(&self) -> bool {
self.len() >= self.capacity()
}
}
// 实现 LRU(最近最少使用)缓存策略
#[derive(Debug)]
struct LruCacheStrategy<K> {
capacity: usize,
keys: Vec<K>,
key_map: HashMap<K, usize>,
}
impl<K: std::hash::Hash + Eq + Clone> LruCacheStrategy<K> {
fn new(capacity: usize) -> Self {
LruCacheStrategy {
capacity,
keys: Vec::new(),
key_map: HashMap::new(),
}
}
}
impl<K: std::hash::Hash + Eq + Clone, V> CacheStrategy<K, V> for LruCacheStrategy<K> {
fn on_insert(&mut self, key: K, _value: V) {
if self.key_map.contains_key(&key) {
// 如果 key 已存在,移动到 vec 的末尾(最近使用)
let index = self.key_map[&key];
self.keys.remove(index);
self.keys.push(key.clone());
self.key_map.insert(key, self.keys.len() - 1);
} else {
// 如果 key 不存在,添加到 vec 的末尾
self.key_map.insert(key.clone(), self.keys.len());
self.keys.push(key);
}
}
fn on_access(&mut self, key: &K) {
if let Some(index) = self.key_map.get(key) {
let key_clone = key.clone();
self.keys.remove(*index);
self.keys.push(key_clone);
self.key_map.insert(key_clone, self.keys.len() - 1);
}
}
fn on_delete(&mut self) -> Option<K> {
if self.is_full() {
// 删除 vec 的第一个元素(最少使用)
let key = self.keys.remove(0);
self.key_map.remove(&key);
Some(key)
} else {
None
}
}
fn len(&self) -> usize {
self.keys.len()
}
fn capacity(&self) -> usize {
self.capacity
}
}
// 定义存储介质的 trait
trait CacheStorage<K, V> {
// 插入数据
fn insert(&mut self, key: K, value: V);
// 获取数据
fn get(&mut self, key: &K) -> Option<&V>;
// 删除数据
fn remove(&mut self, key: &K) -> Option<V>;
// 获取存储的大小
fn len(&self) -> usize;
}
// 实现内存存储介质
#[derive(Debug)]
struct MemoryStorage<K, V> {
data: HashMap<K, V>,
}
impl<K: std::hash::Hash + Eq, V> MemoryStorage<K, V> {
fn new() -> Self {
MemoryStorage {
data: HashMap::new(),
}
}
}
impl<K: std::hash::Hash + Eq, V> CacheStorage<K, V> for MemoryStorage<K, V> {
fn insert(&mut self, key: K, value: V) {
self.data.insert(key, value);
}
fn get(&mut self, key: &K) -> Option<&V> {
self.data.get(key)
}
fn remove(&mut self, key: &K) -> Option<V> {
self.data.remove(key)
}
fn len(&self) -> usize {
self.data.len()
}
}
// 定义通用的缓存系统
#[derive(Debug)]
struct Cache<K, V, S: CacheStrategy<K, V>, T: CacheStorage<K, V>> {
strategy: S,
storage: T,
}
impl<K, V, S: CacheStrategy<K, V>, T: CacheStorage<K, V>> Cache<K, V, S, T> {
fn new(strategy: S, storage: T) -> Self {
Cache { strategy, storage }
}
fn insert(&mut self, key: K, value: V) {
// 如果缓存已满,删除最少使用的 key
if let Some(delete_key) = self.strategy.on_delete() {
self.storage.remove(&delete_key);
}
// 插入新数据
self.storage.insert(key.clone(), value);
self.strategy.on_insert(key, value);
}
fn get(&mut self, key: &K) -> Option<&V> {
if let Some(value) = self.storage.get(key) {
self.strategy.on_access(key);
Some(value)
} else {
None
}
}
fn len(&self) -> usize {
self.storage.len()
}
fn capacity(&self) -> usize {
self.strategy.capacity()
}
}
fn main() {
// 实例化一个容量为 3 的 LRU 内存缓存
let mut cache = Cache::new(LruCacheStrategy::new(3), MemoryStorage::new());
// 插入数据
cache.insert("key1", "value1");
cache.insert("key2", "value2");
cache.insert("key3", "value3");
println!("插入 3 个数据后的缓存状态:{:?}", cache);
// 访问 key1,使其成为最近使用的
let value1 = cache.get(&"key1").unwrap();
println!("访问 key1,值为:{}", value1);
println!("访问 key1 后的缓存状态:{:?}", cache);
// 插入 key4,导致 key2 被删除(最少使用)
cache.insert("key4", "value4");
println!("插入 key4 后的缓存状态:{:?}", cache);
println!("cache 中是否包含 key2: {}", cache.get(&"key2").is_some());
}
💡 场景分析:需要编写一个简单的 HTTP 请求解析器,支持解析 GET 和 POST 请求,包括请求方法、路径、版本、请求头和请求体。
⌨️ 代码示例:
use std::collections::HashMap;
// 定义 HTTP 方法的枚举
#[derive(Debug, PartialEq)]
enum HttpMethod {
GET,
POST,
PUT,
DELETE,
OTHER(String),
}
impl From<String> for HttpMethod {
fn from(method_str: String) -> Self {
match method_str.to_uppercase().as_str() {
"GET" => HttpMethod::GET,
"POST" => HttpMethod::POST,
"PUT" => HttpMethod::PUT,
"DELETE" => HttpMethod::DELETE,
_ => HttpMethod::OTHER(method_str),
}
}
}
// 定义 HTTP 版本的枚举
#[derive(Debug, PartialEq)]
enum HttpVersion {
HTTP10,
HTTP11,
HTTP20,
OTHER(String),
}
impl From<String> for HttpVersion {
fn from(version_str: String) -> Self {
match version_str.to_uppercase().as_str() {
"HTTP/1.0" => HttpVersion::HTTP10,
"HTTP/1.1" => HttpVersion::HTTP11,
"HTTP/2.0" => HttpVersion::HTTP20,
_ => HttpVersion::OTHER(version_str),
}
}
}
// 定义 HTTP 请求结构体
#[derive(Debug)]
struct HttpRequest {
method: HttpMethod,
path: String,
version: HttpVersion,
headers: HashMap<String, String>,
body: Vec<u8>,
}
impl HttpRequest {
fn new() -> Self {
HttpRequest {
method: HttpMethod::OTHER("".to_string()),
path: "".to_string(),
version: HttpVersion::OTHER("".to_string()),
headers: HashMap::new(),
body: Vec::new(),
}
}
// 解析 HTTP 请求字符串
fn parse(raw_request: &[u8]) -> Result<Self, String> {
let raw_str = String::from_utf8_lossy(raw_request).into_owned();
let mut lines = raw_str.split("\r\n");
// 解析请求行
let request_line = match lines.next() {
Some(line) => line.trim(),
None => return Err("无效的请求行".to_string()),
};
let request_parts: Vec<&str> = request_line.split_whitespace().collect();
if request_parts.len() != 3 {
return Err("请求行格式错误".to_string());
}
let method = HttpMethod::from(request_parts[0].to_string());
let path = request_parts[1].to_string();
let version = HttpVersion::from(request_parts[2].to_string());
// 解析请求头
let mut headers = HashMap::new();
loop {
let line = match lines.next() {
Some(line) => line.trim(),
None => break,
};
if line.is_empty() {
break;
}
let header_parts: Vec<&str> = line.splitn(2, ':').collect();
if header_parts.len() != 2 {
return Err("请求头格式错误".to_string());
}
let header_name = header_parts[0].trim().to_lowercase();
let header_value = header_parts[1].trim().to_string();
headers.insert(header_name, header_value);
}
// 解析请求体
let mut body = Vec::new();
if let Some(content_length_str) = headers.get("content-length") {
let content_length = match content_length_str.parse::<usize>() {
Ok(len) => len,
Err(_) => return Err("Content-Length 格式错误".to_string()),
};
// 计算请求体的起始位置
let request_line_length = request_line.len() + 2; // 加上\r\n
let headers_length: usize = headers.iter().map(|(name, value)| name.len() + 2 + value.len() + 2).sum();
let start_index = request_line_length + headers_length + 2; // 加上最后的空行\r\n
if raw_request.len() >= start_index + content_length {
body.extend_from_slice(&raw_request[start_index..start_index + content_length]);
}
}
Ok(HttpRequest { method, path, version, headers, body })
}
// 打印 HTTP 请求信息
fn print_info(&self) {
println!("请求方法:{:?}", self.method);
println!("请求路径:{}", self.path);
println!("HTTP 版本:{:?}", self.version);
println!("请求头:");
for (name, value) in self.headers.iter() {
println!(" {}: {}", name, value);
}
println!("请求体长度:{}", self.body.len());
if !self.body.is_empty() {
if let Ok(body_str) = String::from_utf8(self.body.clone()) {
println!("请求体内容:{}", body_str);
} else {
println!("请求体内容:二进制数据");
}
}
}
}
fn main() {
// 示例 1:简单的 GET 请求
let raw_get_request = b"GET /index.html HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Rust HTTP Client\r\n\r\n";
let get_request = HttpRequest::parse(raw_get_request).expect("解析 GET 请求失败");
println!("---------- GET 请求信息 ----------");
get_request.print_info();
println!("----------");
// 示例 2:带请求体的 POST 请求
let raw_post_request = b"POST /submit HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Rust HTTP Client\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: 25\r\n\r\nname=张三&[email protected]";
let post_request = HttpRequest::parse(raw_post_request).expect("解析 POST 请求失败");
println!("---------- POST 请求信息 ----------");
post_request.print_info();
}
问题现象:尝试将包含 Self 类型或泛型参数的方法的 trait 转换为 trait 对象时,会导致编译错误。
解决方案:
问题现象:函数调用时实参类型不符合泛型结构体/枚举的约束条件,导致编译错误。
解决方案:
std::marker::PhantomData 来标记未使用的泛型参数问题现象:使用 serde 等库时,字段属性配置不当导致序列化失败。
解决方案:
#[serde(skip_deserializing)] 或 #[serde(skip_serializing)] 来跳过某些字段的序列化/反序列化#[serde(default)] 来设置默认值✅ 掌握了泛型结构体/枚举的定义与应用,实现了类型安全的代码复用 ✅ 精通了 trait 的高级应用,包括关联类型、默认实现、trait 对象的动态分发机制 ✅ 优化了模式匹配的代码,深入学习了嵌套模式匹配、判别式处理 ✅ 丰富了自定义类型的功能,了解了派生宏的高级配置和字段验证方法 ✅ 结合真实场景编写了两个实用的代码案例:通用数据缓存系统和 HTTP 请求解析器
下一篇文章,我们将深入学习 Rust 的集合类型与迭代器,包括标准库中的各种集合类型(如 Vec、HashMap、HashSet)的高级用法,以及迭代器的链式操作、自定义迭代器的实现,通过这些知识我们将能够更高效地处理复杂的数据结构。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online