diff --git a/03.md b/03.md new file mode 100644 index 0000000..ee32c69 --- /dev/null +++ b/03.md @@ -0,0 +1,390 @@ +# 第3章 类型系统实战 + +> 本章要点:类型系统基础、Option/Result、泛型、trait、错误处理 + +## 3.1 类型系统概述 + +Rust的类型系统是其最强大的特性之一,它在编译时就能捕获大量潜在的错误。让我们深入了解Rust类型系统的核心特性。 + +### 3.1.1 Rust类型系统特点 + +Rust的类型系统具有以下关键特点: + +1. **静态类型**:所有变量在编译时都必须有明确的类型 +2. **强类型**:类型之间没有隐式转换 +3. **类型推断**:在大多数情况下编译器可以推断出类型 +4. **零成本抽象**:类型系统的抽象在运行时没有性能开销 + +示例:类型推断与显式类型标注 +```rust +// 类型推断 +let x = 42; // 编译器推断为 i32 +let y = 3.14; // 编译器推断为 f64 + +// 显式类型标注 +let x: i32 = 42; +let y: f64 = 3.14; +``` + +### 3.1.2 类型安全保证 + +Rust的类型系统提供了强大的安全保证: + +1. **内存安全**:通过所有权系统防止内存泄漏和数据竞争 +2. **空值安全**:使用Option枚举代替null +3. **线程安全**:通过Send和Sync trait保证 +4. **错误处理**:使用Result类型强制错误处理 + +## 3.2 Option和Result实战 + +### 3.2.1 Option枚举详解 + +Option是Rust处理可能为空值的标准方式: + +```rust +enum Option { + Some(T), + None, +} + +// Option使用示例 +fn find_user(id: i32) -> Option { + if id > 0 { + Some(String::from("User found")) + } else { + None + } +} + +// 模式匹配处理Option +match find_user(1) { + Some(user) => println!("找到用户: {}", user), + None => println!("用户不存在"), +} +``` + +### 3.2.2 模式匹配最佳实践 + +模式匹配是处理Option和Result的强大工具: + +```rust +let maybe_value = Some(42); + +// 1. match表达式 +match maybe_value { + Some(value) => println!("值是: {}", value), + None => println!("没有值"), +} + +// 2. if let语法 +if let Some(value) = maybe_value { + println!("值是: {}", value); +} + +// 3. while let循环 +let mut stack = Vec::new(); +stack.push(1); +stack.push(2); + +while let Some(top) = stack.pop() { + println!("栈顶值: {}", top); +} +``` + +### 3.2.3 Result错误处理模式 + +Result用于处理可能失败的操作: + +```rust +enum Result { + Ok(T), + Err(E), +} + +// Result使用示例 +fn divide(x: f64, y: f64) -> Result { + if y == 0.0 { + Err(String::from("除数不能为零")) + } else { + Ok(x / y) + } +} + +// 使用match处理Result +match divide(10.0, 2.0) { + Ok(result) => println!("结果: {}", result), + Err(e) => println!("错误: {}", e), +} +``` + +### 3.2.4 ?操作符使用技巧 + +`?`操作符简化了错误处理: + +```rust +use std::fs::File; +use std::io::{self, Read}; + +fn read_file_contents(path: &str) -> Result { + let mut file = File::open(path)?; // 如果发生错误,立即返回错误 + let mut contents = String::new(); + file.read_to_string(&mut contents)?; // 同上 + Ok(contents) +} +``` + +## 3.3 泛型编程精要 + +### 3.3.1 泛型函数和结构体 + +泛型允许我们编写灵活可复用的代码: + +```rust +// 泛型函数 +fn largest(list: &[T]) -> &T { + let mut largest = &list[0]; + for item in list.iter() { + if item > largest { + largest = item; + } + } + largest +} + +// 泛型结构体 +struct Point { + x: T, + y: T, +} + +impl Point { + fn new(x: T, y: T) -> Self { + Point { x, y } + } +} +``` + +### 3.3.2 泛型约束 + +通过trait约束限制泛型类型: + +```rust +use std::fmt::Display; + +fn print_pair(x: T, y: T) { + println!("x = {}, y = {}", x, y); +} + +// 多重约束 +fn describe(value: T) { + let copy = value.clone(); + println!("值是: {}", copy); +} + +// where子句 +fn complex_function(t: T, u: U) -> i32 +where + T: Display + Clone, + U: Clone + Default, +{ + // 实现 + 42 +} +``` + +### 3.3.3 关联类型 + +在trait中使用关联类型提高代码清晰度: + +```rust +trait Container { + type Item; + fn get(&self) -> Option<&Self::Item>; + fn insert(&mut self, item: Self::Item); +} + +struct Stack { + items: Vec, +} + +impl Container for Stack { + type Item = T; + + fn get(&self) -> Option<&Self::Item> { + self.items.last() + } + + fn insert(&mut self, item: Self::Item) { + self.items.push(item); + } +} +``` + +## 3.4 Trait系统深入 + +### 3.4.1 Trait定义和实现 + +trait定义了类型可以实现的行为: + +```rust +// 定义trait +trait Describable { + fn describe(&self) -> String; + + // 带有默认实现的方法 + fn default_description(&self) -> String { + String::from("这是一个可描述的对象") + } +} + +// 为类型实现trait +struct Person { + name: String, + age: u32, +} + +impl Describable for Person { + fn describe(&self) -> String { + format!("{},{}岁", self.name, self.age) + } +} +``` + +### 3.4.2 Trait对象 + +使用trait对象实现运行时多态: + +```rust +trait Animal { + fn make_sound(&self) -> String; +} + +struct Dog { + name: String, +} + +struct Cat { + name: String, +} + +impl Animal for Dog { + fn make_sound(&self) -> String { + format!("{}: 汪汪!", self.name) + } +} + +impl Animal for Cat { + fn make_sound(&self) -> String { + format!("{}: 喵喵!", self.name) + } +} + +// 使用trait对象 +fn animal_sounds(animals: Vec>) { + for animal in animals { + println!("{}", animal.make_sound()); + } +} +``` + +## 3.5 实战项目:文件处理工具库 + +在本章的最后,我们将实现一个文件处理工具库,整合所学的类型系统知识。完整代码请参考`examples/ex03`目录。 + +### 3.5.1 核心特性 + +1. 强类型的文件操作API +2. 完整的错误处理链 +3. 可扩展的插件系统(通过trait实现) +4. 泛型化的数据处理接口 + +### 3.5.2 示例代码片段 + +```rust +// error.rs +#[derive(Debug)] +pub enum FileError { + NotFound(String), + PermissionDenied(String), + ReadError(String), + WriteError(String), +} + +// traits.rs +pub trait FileProcessor { + type Error; + fn process(&self, content: &str) -> Result; +} + +// file_ops.rs +pub struct SafeFileReader { + path: String, +} + +impl SafeFileReader { + pub fn new(path: String) -> Self { + SafeFileReader { path } + } + + pub fn read_and_process( + &self, + processor: &P + ) -> Result { + let content = std::fs::read_to_string(&self.path) + .map_err(|e| FileError::ReadError(e.to_string()))?; + + processor.process(&content) + .map_err(|_| FileError::ReadError("处理失败".to_string())) + } +} +``` + +### 3.5.3 使用示例 + +```rust +// main.rs +struct UpperCaseProcessor; + +impl FileProcessor for UpperCaseProcessor { + type Error = String; + + fn process(&self, content: &str) -> Result { + Ok(content.to_uppercase()) + } +} + +fn main() -> Result<(), FileError> { + let reader = SafeFileReader::new("input.txt".to_string()); + let processor = UpperCaseProcessor; + + let processed_content = reader.read_and_process(&processor)?; + println!("处理后的内容:\n{}", processed_content); + + Ok(()) +} +``` + +## 本章小结 + +在本章中,我们深入探讨了Rust的类型系统,包括: + +1. 类型系统的基础概念和安全保证 +2. Option和Result的实际应用 +3. 泛型编程的核心概念 +4. trait系统的深入使用 +5. 实战项目中的综合应用 + +通过文件处理工具库的实现,我们看到了如何将这些概念组合使用,创建类型安全、错误处理完善的实用程序。 + +## 练习题 + +1. 实现一个泛型的Stack数据结构,包含push、pop和peek方法 +2. 为文件处理工具库添加新的处理器,如行号添加器、关键词统计器等 +3. 使用trait对象实现插件系统,允许在运行时动态加载不同的文件处理器 +4. 扩展错误类型,添加更多具体的错误场景和错误信息 + +## 扩展阅读 + +- [Rust官方文档:泛型](https://doc.rust-lang.org/book/ch10-00-generics.html) +- [Rust官方文档:trait](https://doc.rust-lang.org/book/ch10-02-traits.html) +- [Rust错误处理最佳实践](https://rust-lang.github.io/rust-clippy/master/index.html#error) \ No newline at end of file diff --git a/examples/ex03/Cargo.toml b/examples/ex03/Cargo.toml new file mode 100644 index 0000000..7ed9727 --- /dev/null +++ b/examples/ex03/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "file_processor" +version = "0.1.0" +edition = "2021" +authors = ["Rust实战精要"] +description = "第3章示例:文件处理工具库" + +[dependencies] +thiserror = "1.0" # 简化错误定义 +anyhow = "1.0" # 错误处理工具 \ No newline at end of file diff --git a/examples/ex03/src/error.rs b/examples/ex03/src/error.rs new file mode 100644 index 0000000..ae84ae8 --- /dev/null +++ b/examples/ex03/src/error.rs @@ -0,0 +1,64 @@ +use std::fmt; +use std::io; +use thiserror::Error; + +/// 文件处理错误类型 +#[derive(Error, Debug)] +pub enum FileError { + /// 文件未找到错误 + #[error("文件未找到: {0}")] + NotFound(String), + + /// 权限错误 + #[error("权限不足: {0}")] + PermissionDenied(String), + + /// 读取错误 + #[error("读取文件失败: {0}")] + ReadError(String), + + /// 写入错误 + #[error("写入文件失败: {0}")] + WriteError(String), + + /// 处理错误 + #[error("处理内容失败: {0}")] + ProcessError(String), + + /// IO错误 + #[error("IO错误: {0}")] + IoError(#[from] io::Error), + + /// 未知错误 + #[error("未知错误: {0}")] + Unknown(String), +} + +// 实现Display trait,提供更友好的错误信息 +impl fmt::Display for FileError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FileError::NotFound(path) => write!(f, "找不到文件: {}", path), + FileError::PermissionDenied(path) => write!(f, "没有权限访问文件: {}", path), + FileError::ReadError(msg) => write!(f, "读取文件时出错: {}", msg), + FileError::WriteError(msg) => write!(f, "写入文件时出错: {}", msg), + FileError::ProcessError(msg) => write!(f, "处理文件内容时出错: {}", msg), + FileError::IoError(err) => write!(f, "IO错误: {}", err), + FileError::Unknown(msg) => write!(f, "未知错误: {}", msg), + } + } +} + +// 从字符串创建错误的便捷方法 +impl From<&str> for FileError { + fn from(error: &str) -> Self { + FileError::Unknown(error.to_string()) + } +} + +// 从字符串创建错误的便捷方法 +impl From for FileError { + fn from(error: String) -> Self { + FileError::Unknown(error) + } +} \ No newline at end of file diff --git a/examples/ex03/src/file_ops.rs b/examples/ex03/src/file_ops.rs new file mode 100644 index 0000000..6fb2641 --- /dev/null +++ b/examples/ex03/src/file_ops.rs @@ -0,0 +1,197 @@ +use std::fs::{self, File}; +use std::io::{self, Read, Write}; +use std::path::Path; + +use crate::error::FileError; +use crate::traits::{ContentAnalyzer, ContentTransformer, FileProcessor, LineFilter}; + +/// 安全的文件读取器 +pub struct SafeFileReader { + path: String, +} + +impl SafeFileReader { + /// 创建新的文件读取器 + /// + /// # 参数 + /// * `path` - 文件路径 + pub fn new(path: String) -> Self { + SafeFileReader { path } + } + + /// 读取文件内容 + /// + /// # 返回 + /// * `Result` - 文件内容或错误 + pub fn read(&self) -> Result { + let path = Path::new(&self.path); + + if !path.exists() { + return Err(FileError::NotFound(self.path.clone())); + } + + fs::read_to_string(path) + .map_err(|e| match e.kind() { + io::ErrorKind::PermissionDenied => FileError::PermissionDenied(self.path.clone()), + _ => FileError::ReadError(e.to_string()), + }) + } + + /// 读取并处理文件内容 + /// + /// # 参数 + /// * `processor` - 实现了FileProcessor trait的处理器 + /// + /// # 返回 + /// * `Result` - 处理后的内容或错误 + pub fn read_and_process

(&self, processor: &P) -> Result + where + P: FileProcessor, + { + let content = self.read()?; + + processor.process(&content) + .map_err(|e| FileError::ProcessError(format!("{:?}", e))) + } + + /// 读取并分析文件内容 + /// + /// # 参数 + /// * `analyzer` - 实现了ContentAnalyzer trait的分析器 + /// + /// # 返回 + /// * `Result` - 分析结果或错误 + pub fn read_and_analyze(&self, analyzer: &A) -> Result + where + A: ContentAnalyzer, + { + let content = self.read()?; + + analyzer.analyze(&content) + .map_err(|e| FileError::ProcessError(format!("{:?}", e))) + } + + /// 读取并过滤文件内容 + /// + /// # 参数 + /// * `filter` - 实现了LineFilter trait的过滤器 + /// + /// # 返回 + /// * `Result` - 过滤后的内容或错误 + pub fn read_and_filter(&self, filter: &F) -> Result + where + F: LineFilter, + { + let content = self.read()?; + + let filtered = content + .lines() + .filter(|line| filter.should_keep(line)) + .collect::>() + .join("\n"); + + Ok(filtered) + } +} + +/// 安全的文件写入器 +pub struct SafeFileWriter { + path: String, +} + +impl SafeFileWriter { + /// 创建新的文件写入器 + /// + /// # 参数 + /// * `path` - 文件路径 + pub fn new(path: String) -> Self { + SafeFileWriter { path } + } + + /// 写入内容到文件 + /// + /// # 参数 + /// * `content` - 要写入的内容 + /// + /// # 返回 + /// * `Result<(), FileError>` - 成功或错误 + pub fn write(&self, content: &str) -> Result<(), FileError> { + let mut file = File::create(&self.path) + .map_err(|e| match e.kind() { + io::ErrorKind::PermissionDenied => FileError::PermissionDenied(self.path.clone()), + _ => FileError::WriteError(e.to_string()), + })?; + + file.write_all(content.as_bytes()) + .map_err(|e| FileError::WriteError(e.to_string()))?; + + Ok(()) + } + + /// 转换并写入内容到文件 + /// + /// # 参数 + /// * `content` - 原始内容 + /// * `transformer` - 实现了ContentTransformer trait的转换器 + /// + /// # 返回 + /// * `Result<(), FileError>` - 成功或错误 + pub fn transform_and_write(&self, content: &str, transformer: &T) -> Result<(), FileError> + where + T: ContentTransformer, + { + let transformed = transformer.transform(content) + .map_err(|e| FileError::ProcessError(format!("{:?}", e)))?; + + self.write(&transformed) + } +} + +/// 文件处理管道 +/// +/// 允许链式调用多个处理器 +pub struct ProcessingPipeline<'a> { + processors: Vec<&'a dyn FileProcessor>, +} + +impl<'a> ProcessingPipeline<'a> { + /// 创建新的处理管道 + pub fn new() -> Self { + ProcessingPipeline { + processors: Vec::new(), + } + } + + /// 添加处理器到管道 + /// + /// # 参数 + /// * `processor` - 实现了FileProcessor trait的处理器 + pub fn add_processor(&mut self, processor: &'a dyn FileProcessor) -> &mut Self { + self.processors.push(processor); + self + } + + /// 执行处理管道 + /// + /// # 参数 + /// * `content` - 要处理的内容 + /// + /// # 返回 + /// * `Result` - 处理后的内容或错误 + pub fn process(&self, content: &str) -> Result { + let mut result = content.to_string(); + + for processor in &self.processors { + result = processor.process(&result) + .map_err(|e| FileError::ProcessError(e))?; + } + + Ok(result) + } +} + +impl<'a> Default for ProcessingPipeline<'a> { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file diff --git a/examples/ex03/src/main.rs b/examples/ex03/src/main.rs new file mode 100644 index 0000000..9c08cdb --- /dev/null +++ b/examples/ex03/src/main.rs @@ -0,0 +1,215 @@ +mod error; +mod file_ops; +mod traits; +mod utils; + +use std::collections::HashMap; +use std::fs::File; +use std::io::Write; + +use error::FileError; +use file_ops::{ProcessingPipeline, SafeFileReader, SafeFileWriter}; +use traits::{ContentAnalyzer, FileProcessor}; +use utils::{ + EmptyLineFilter, KeywordFilter, LineNumberProcessor, LowerCaseProcessor, + MarkdownToHtmlTransformer, TextStatistics, TextStatisticsAnalyzer, UpperCaseProcessor, + WordFrequencyAnalyzer, +}; + +/// 创建示例文件 +fn create_sample_file(path: &str, content: &str) -> Result<(), FileError> { + let mut file = File::create(path).map_err(FileError::IoError)?; + file.write_all(content.as_bytes()).map_err(FileError::IoError)?; + Ok(()) +} + +/// 展示文件处理器的使用 +fn demo_file_processors() -> Result<(), FileError> { + println!("\n=== 文件处理器演示 ==="); + + // 创建示例文件 + let sample_path = "sample.txt"; + let sample_content = "这是第一行文本。\n这是第二行文本。\n\n这是第四行文本。"; + create_sample_file(sample_path, sample_content)?; + println!("创建了示例文件: {}", sample_path); + + // 创建文件读取器 + let reader = SafeFileReader::new(sample_path.to_string()); + + // 使用大写处理器 + let upper_processor = UpperCaseProcessor; + let upper_result = reader.read_and_process(&upper_processor)?; + println!("\n使用{}:\n{}", upper_processor.name(), upper_result); + + // 使用小写处理器 + let lower_processor = LowerCaseProcessor; + let lower_result = reader.read_and_process(&lower_processor)?; + println!("\n使用{}:\n{}", lower_processor.name(), lower_result); + + // 使用行号处理器 + let line_num_processor = LineNumberProcessor; + let line_num_result = reader.read_and_process(&line_num_processor)?; + println!("\n使用{}:\n{}", line_num_processor.name(), line_num_result); + + // 使用空行过滤器 + let empty_line_filter = EmptyLineFilter; + let filtered_content = reader.read_and_filter(&empty_line_filter)?; + println!("\n使用空行过滤器:\n{}", filtered_content); + + // 使用关键词过滤器 + let keyword_filter = KeywordFilter::new("第二".to_string(), true); + let keyword_filtered = reader.read_and_filter(&keyword_filter)?; + println!("\n使用关键词过滤器 (关键词: '第二'):\n{}", keyword_filtered); + + Ok(()) +} + +/// 展示处理管道的使用 +fn demo_processing_pipeline() -> Result<(), FileError> { + println!("\n=== 处理管道演示 ==="); + + // 创建示例文件 + let sample_path = "pipeline_sample.txt"; + let sample_content = "第一行\n第二行\n第三行\n第四行"; + create_sample_file(sample_path, sample_content)?; + println!("创建了示例文件: {}", sample_path); + + // 创建文件读取器 + let reader = SafeFileReader::new(sample_path.to_string()); + let content = reader.read()?; + + // 创建处理管道 + let mut pipeline = ProcessingPipeline::new(); + + // 添加处理器 + pipeline + .add_processor(&LineNumberProcessor) + .add_processor(&UpperCaseProcessor); + + // 执行管道 + let result = pipeline.process(&content)?; + println!("\n处理管道结果 (行号 -> 大写):\n{}", result); + + Ok(()) +} + +/// 展示内容分析器的使用 +fn demo_content_analyzers() -> Result<(), FileError> { + println!("\n=== 内容分析器演示 ==="); + + // 创建示例文件 + let sample_path = "analyzer_sample.txt"; + let sample_content = "这是一个示例文本文件。\n它包含多行文本。\n\n这些文本将被分析。\n我们可以统计单词频率和其他指标。"; + create_sample_file(sample_path, sample_content)?; + println!("创建了示例文件: {}", sample_path); + + // 创建文件读取器 + let reader = SafeFileReader::new(sample_path.to_string()); + + // 使用文本统计分析器 + let stats_analyzer = TextStatisticsAnalyzer; + let stats: TextStatistics = reader.read_and_analyze(&stats_analyzer)?; + + println!("\n文本统计结果:"); + println!("总行数: {}", stats.line_count); + println!("总字数: {}", stats.word_count); + println!("总字符数: {}", stats.char_count); + println!("非空行数: {}", stats.non_empty_lines); + + // 使用单词频率分析器 + let word_analyzer = WordFrequencyAnalyzer; + let word_counts: HashMap = reader.read_and_analyze(&word_analyzer)?; + + println!("\n单词频率统计 (前5个):"); + let mut words: Vec<(&String, &usize)> = word_counts.iter().collect(); + words.sort_by(|a, b| b.1.cmp(a.1)); + + for (i, (word, count)) in words.iter().take(5).enumerate() { + println!("{}. {} - 出现{}次", i + 1, word, count); + } + + Ok(()) +} + +/// 展示内容转换器的使用 +fn demo_content_transformer() -> Result<(), FileError> { + println!("\n=== 内容转换器演示 ==="); + + // 创建示例Markdown文件 + let md_path = "sample.md"; + let md_content = "# 示例标题\n\n## 小标题\n\n这是一段普通文本。\n\n### 小小标题\n\n这是另一段文本。"; + create_sample_file(md_path, md_content)?; + println!("创建了示例Markdown文件: {}", md_path); + + // 创建文件读取器和写入器 + let reader = SafeFileReader::new(md_path.to_string()); + let writer = SafeFileWriter::new("sample.html".to_string()); + + // 读取Markdown内容 + let content = reader.read()?; + + // 使用Markdown到HTML转换器 + let transformer = MarkdownToHtmlTransformer; + writer.transform_and_write(&content, &transformer)?; + + println!("\nMarkdown已转换为HTML并保存到 sample.html"); + + Ok(()) +} + +/// 自定义处理器示例 +struct ReverseLineProcessor; + +impl FileProcessor for ReverseLineProcessor { + type Error = String; + + fn process(&self, content: &str) -> Result { + let reversed = content + .lines() + .map(|line| line.chars().rev().collect::()) + .collect::>() + .join("\n"); + + Ok(reversed) + } + + fn name(&self) -> String { + String::from("行反转处理器") + } + + fn description(&self) -> String { + String::from("将每行文本反转") + } +} + +fn main() -> Result<(), FileError> { + println!("=== 文件处理工具库演示 ==="); + + // 演示文件处理器 + demo_file_processors()?; + + // 演示处理管道 + demo_processing_pipeline()?; + + // 演示内容分析器 + demo_content_analyzers()?; + + // 演示内容转换器 + demo_content_transformer()?; + + // 使用自定义处理器 + println!("\n=== 自定义处理器演示 ==="); + let sample_path = "custom_sample.txt"; + let sample_content = "这是第一行\n这是第二行\n这是第三行"; + create_sample_file(sample_path, sample_content)?; + + let reader = SafeFileReader::new(sample_path.to_string()); + let reverse_processor = ReverseLineProcessor; + let result = reader.read_and_process(&reverse_processor)?; + + println!("\n使用{}:\n{}", reverse_processor.name(), result); + + println!("\n所有演示完成!"); + + Ok(()) +} \ No newline at end of file diff --git a/examples/ex03/src/traits.rs b/examples/ex03/src/traits.rs new file mode 100644 index 0000000..0bc2a47 --- /dev/null +++ b/examples/ex03/src/traits.rs @@ -0,0 +1,76 @@ +use std::fmt::Debug; + +/// 文件处理器trait +/// +/// 定义了处理文件内容的通用接口 +pub trait FileProcessor { + /// 处理器可能产生的错误类型 + type Error: Debug; + + /// 处理文件内容的方法 + /// + /// # 参数 + /// * `content` - 要处理的文件内容 + /// + /// # 返回 + /// * `Result` - 处理后的内容或错误 + fn process(&self, content: &str) -> Result; + + /// 处理器名称(默认实现) + fn name(&self) -> String { + String::from("默认处理器") + } + + /// 处理器描述(默认实现) + fn description(&self) -> String { + String::from("处理文件内容") + } +} + +/// 文件过滤器trait +/// +/// 用于过滤文件内容的行 +pub trait LineFilter { + /// 判断一行是否应该被保留 + /// + /// # 参数 + /// * `line` - 要判断的行 + /// + /// # 返回 + /// * `bool` - true表示保留,false表示过滤掉 + fn should_keep(&self, line: &str) -> bool; +} + +/// 文件转换器trait +/// +/// 用于转换文件内容 +pub trait ContentTransformer { + /// 转换器可能产生的错误类型 + type Error: Debug; + + /// 转换文件内容 + /// + /// # 参数 + /// * `content` - 要转换的内容 + /// + /// # 返回 + /// * `Result` - 转换后的内容或错误 + fn transform(&self, content: &str) -> Result; +} + +/// 文件分析器trait +/// +/// 用于分析文件内容并生成统计信息 +pub trait ContentAnalyzer { + /// 分析器可能产生的错误类型 + type Error: Debug; + + /// 分析文件内容 + /// + /// # 参数 + /// * `content` - 要分析的内容 + /// + /// # 返回 + /// * `Result` - 分析结果或错误 + fn analyze(&self, content: &str) -> Result; +} \ No newline at end of file diff --git a/examples/ex03/src/utils.rs b/examples/ex03/src/utils.rs new file mode 100644 index 0000000..bb6afff --- /dev/null +++ b/examples/ex03/src/utils.rs @@ -0,0 +1,240 @@ +use std::collections::HashMap; +use std::path::Path; + +use crate::traits::{ContentAnalyzer, ContentTransformer, FileProcessor, LineFilter}; + +/// 大写转换处理器 +pub struct UpperCaseProcessor; + +impl FileProcessor for UpperCaseProcessor { + type Error = String; + + fn process(&self, content: &str) -> Result { + Ok(content.to_uppercase()) + } + + fn name(&self) -> String { + String::from("大写转换处理器") + } + + fn description(&self) -> String { + String::from("将文本内容转换为大写") + } +} + +/// 小写转换处理器 +pub struct LowerCaseProcessor; + +impl FileProcessor for LowerCaseProcessor { + type Error = String; + + fn process(&self, content: &str) -> Result { + Ok(content.to_lowercase()) + } + + fn name(&self) -> String { + String::from("小写转换处理器") + } + + fn description(&self) -> String { + String::from("将文本内容转换为小写") + } +} + +/// 行号添加处理器 +pub struct LineNumberProcessor; + +impl FileProcessor for LineNumberProcessor { + type Error = String; + + fn process(&self, content: &str) -> Result { + let numbered = content + .lines() + .enumerate() + .map(|(i, line)| format!("{}: {}", i + 1, line)) + .collect::>() + .join("\n"); + + Ok(numbered) + } + + fn name(&self) -> String { + String::from("行号添加处理器") + } + + fn description(&self) -> String { + String::from("为每行添加行号") + } +} + +/// 空行过滤器 +pub struct EmptyLineFilter; + +impl LineFilter for EmptyLineFilter { + fn should_keep(&self, line: &str) -> bool { + !line.trim().is_empty() + } +} + +/// 关键词过滤器 +pub struct KeywordFilter { + keyword: String, + case_sensitive: bool, +} + +impl KeywordFilter { + /// 创建新的关键词过滤器 + /// + /// # 参数 + /// * `keyword` - 要过滤的关键词 + /// * `case_sensitive` - 是否区分大小写 + pub fn new(keyword: String, case_sensitive: bool) -> Self { + KeywordFilter { + keyword, + case_sensitive, + } + } +} + +impl LineFilter for KeywordFilter { + fn should_keep(&self, line: &str) -> bool { + if self.case_sensitive { + line.contains(&self.keyword) + } else { + line.to_lowercase().contains(&self.keyword.to_lowercase()) + } + } +} + +/// Markdown转HTML转换器 +pub struct MarkdownToHtmlTransformer; + +impl ContentTransformer for MarkdownToHtmlTransformer { + type Error = String; + + fn transform(&self, content: &str) -> Result { + // 这里只是一个简单的实现,真实场景可能需要使用专门的Markdown解析库 + let mut html = String::from("\n"); + + for line in content.lines() { + if line.starts_with("# ") { + html.push_str(&format!("

{}

\n", &line[2..])); + } else if line.starts_with("## ") { + html.push_str(&format!("

{}

\n", &line[3..])); + } else if line.starts_with("### ") { + html.push_str(&format!("

{}

\n", &line[4..])); + } else if line.trim().is_empty() { + html.push_str("
\n"); + } else { + html.push_str(&format!("

{}

\n", line)); + } + } + + html.push_str(""); + Ok(html) + } +} + +/// 文本统计分析器 +pub struct TextStatisticsAnalyzer; + +/// 文本统计结果 +#[derive(Debug)] +pub struct TextStatistics { + /// 总行数 + pub line_count: usize, + /// 总字数 + pub word_count: usize, + /// 总字符数 + pub char_count: usize, + /// 非空行数 + pub non_empty_lines: usize, +} + +impl ContentAnalyzer for TextStatisticsAnalyzer { + type Error = String; + + fn analyze(&self, content: &str) -> Result { + let lines: Vec<&str> = content.lines().collect(); + let line_count = lines.len(); + + let char_count = content.chars().count(); + let word_count = content + .split_whitespace() + .count(); + + let non_empty_lines = lines + .iter() + .filter(|line| !line.trim().is_empty()) + .count(); + + Ok(TextStatistics { + line_count, + word_count, + char_count, + non_empty_lines, + }) + } +} + +/// 单词频率分析器 +pub struct WordFrequencyAnalyzer; + +impl ContentAnalyzer> for WordFrequencyAnalyzer { + type Error = String; + + fn analyze(&self, content: &str) -> Result, Self::Error> { + let mut word_counts = HashMap::new(); + + for word in content.split_whitespace() { + // 移除标点符号并转为小写 + let clean_word = word + .chars() + .filter(|c| c.is_alphanumeric()) + .collect::() + .to_lowercase(); + + if !clean_word.is_empty() { + *word_counts.entry(clean_word).or_insert(0) += 1; + } + } + + Ok(word_counts) + } +} + +/// 文件扩展名检查器 +pub struct FileExtensionChecker { + allowed_extensions: Vec, +} + +impl FileExtensionChecker { + /// 创建新的文件扩展名检查器 + /// + /// # 参数 + /// * `extensions` - 允许的扩展名列表 + pub fn new(extensions: Vec) -> Self { + FileExtensionChecker { + allowed_extensions: extensions, + } + } + + /// 检查文件扩展名是否允许 + /// + /// # 参数 + /// * `file_path` - 文件路径 + /// + /// # 返回 + /// * `bool` - 是否允许 + pub fn is_allowed(&self, file_path: &str) -> bool { + let path = Path::new(file_path); + + if let Some(extension) = path.extension() { + if let Some(ext_str) = extension.to_str() { + return self.allowed_extensions.iter().any(|allowed| allowed == ext_str); + } + } + + false + } +} \ No newline at end of file diff --git a/大纲.md b/大纲.md index 2975702..bac6978 100644 --- a/大纲.md +++ b/大纲.md @@ -12,7 +12,7 @@ - 实例:实现安全字符串处理器(避免悬垂指针) **第3章 类型系统实战** -- 提示词:`模式匹配解构Option | Result错误传播?操作符 | 泛型结构体实现 | trait约束实战` +- 提示词:`模式匹配解构Option | Result错误传播?操作符 | 泛型结构体实现 | trait约束实战|unwrap()相关` - 项目:文件读取工具(强错误处理链) **第4章 结构化工程:模块与包**