第12篇:模块与错误处理——组织代码与处理问题 Rust中文编程
第12篇模块与错误处理——组织代码与处理问题作者李金雨联系方式wbtm2718qq.com目标读者Rust中文编程核心理念AI时代必须使用中文编程母语编程阅读效率极高1. 开篇引入本课目标理解什么是模块掌握模块的创建和使用方法理解模块的访问控制掌握错误处理的基本方法理解Result类型的使用掌握错误传播?操作符的使用生活场景引入想象一下你有一个工具箱里面有很多工具锤子和螺丝刀放在一个格子里尺子和铅笔放在另一个格子里剪刀和胶水放在第三个格子里这样你可以很容易地找到你需要的工具。在编程中模块就像工具箱的格子它可以帮助你组织代码使代码更加清晰。另外当你使用工具时可能会遇到一些问题比如锤子坏了或者螺丝刀找不到了。在编程中错误处理就像处理这些问题它可以帮助你优雅地处理程序运行时的错误。预期成果展示通过这节课的学习你将能够创建和使用模块来组织代码理解模块的访问控制使用Result类型处理错误使用?操作符传播错误编写更加健壮的程序2. 概念讲解模块系统什么是模块模块是Rust中组织代码的方式它可以将相关的代码放在一起使代码更加清晰和可维护。模块的创建在Rust中我们使用mod关键字来创建模块fnmain(){// 定义一个模块mod数学工具{pubfn加法(a:i32,b:i32)-i32{ab}pubfn减法(a:i32,b:i32)-i32{a-b}}}模块的访问控制模块中的项默认是私有的只有模块内部可以访问。如果要让模块外部也能访问需要使用pub关键字fnmain(){mod数学工具{pubfn加法(a:i32,b:i32)-i32{// 公共函数ab}fn减法(a:i32,b:i32)-i32{// 私有函数a-b}}println!(1 2 {},数学工具::加法(1,2));// 正确加法是公共的// println!(3 - 1 {}, 数学工具::减法(3, 1)); // 错误减法是私有的}模块的嵌套模块可以嵌套形成层次结构fnmain(){mod工具包{pubmod数学工具{pubfn加法(a:i32,b:i32)-i32{ab}}pubmod字符串工具{pubfn反转(文本:str)-String{文本.chars().rev().collect()}}}println!(1 2 {},工具包::数学工具::加法(1,2));println!(反转{},工具包::字符串工具::反转(你好));}使用use关键字使用use关键字可以简化模块的使用fnmain(){mod工具包{pubmod数学工具{pubfn加法(a:i32,b:i32)-i32{ab}}}use工具包::数学工具::加法;println!(1 2 {},加法(1,2));// 直接使用加法不需要前缀}错误处理Result类型Rust使用ResultT, E类型来处理错误其中T是成功时的类型E是错误时的类型fnmain(){enumResultT,E{Ok(T),// 成功包含成功值Err(E),// 失败包含错误值}}例如fnmain(){fn除法(分子:i32,分母:i32)-Resulti32,String{if分母0{Err(String::from(除数不能为零))}else{Ok(分子/分母)}}match除法(10,2){Ok(结果)println!(结果{},结果),Err(错误)println!(错误{},错误),}match除法(10,0){Ok(结果)println!(结果{},结果),Err(错误)println!(错误{},错误),}}错误传播?操作符?操作符可以简化错误处理它会自动将错误传播给调用者fnmain(){fn读取文件()-ResultString,std::io::Error{let内容std::fs::read_to_string(文件.txt)?;// 如果出错自动返回错误Ok(内容)}match读取文件(){Ok(内容)println!(文件内容{},内容),Err(错误)println!(错误{},错误),}}自定义错误类型你可以定义自己的错误类型fnmain(){#[derive(Debug)]enum计算错误{除数为零,负数平方根,}fn除法(分子:i32,分母:i32)-Resulti32,计算错误{if分母0{Err(计算错误::除数为零)}else{Ok(分子/分母)}}fn平方根(数字:f64)-Resultf64,计算错误{if数字0.0{Err(计算错误::负数平方根)}else{Ok(数字.sqrt())}}match除法(10,0){Ok(结果)println!(结果{},结果),Err(错误)println!(错误{:?},错误),}match平方根(-1.0){Ok(结果)println!(结果{},结果),Err(错误)println!(错误{:?},错误),}}panic!宏panic!宏用于处理不可恢复的错误它会立即终止程序fnmain(){let年龄-5;if年龄0{panic!(年龄不能为负数);}println!(年龄{},年龄);}3. 动手实践基础练习必做创建学生管理模块mod学生管理{pubstruct学生{pub姓名:String,pub年龄:i32,pub成绩:i32,}pubfn添加学生(学生们:mutVec学生,学生:学生){学生们.push(学生);}pubfn删除学生(学生们:mutVec学生,索引:usize)-Option学生{if索引学生们.len(){Some(学生们.remove(索引))}else{None}}pubfn查询学生(学生们:Vec学生,姓名:str)-Option学生{学生们.iter().find(|学生|学生.姓名姓名)}}fnmain(){use学生管理::{学生,添加学生,删除学生,查询学生};letmut学生们vec![];// 添加学生添加学生(mut学生们,学生{姓名:String::from(张三),年龄:15,成绩:95});添加学生(mut学生们,学生{姓名:String::from(李四),年龄:14,成绩:88});// 查询学生ifletSome(学生)查询学生(学生们,张三){println!(找到学生{}年龄{}岁成绩{}分,学生.姓名,学生.年龄,学生.成绩);}// 删除学生ifletSome(学生)删除学生(mut学生们,0){println!(删除学生{},学生.姓名);}// 打印剩余学生println!(剩余学生);for学生in学生们{println!({}{}岁{}分,学生.姓名,学生.年龄,学生.成绩);}}创建文件操作模块处理文件读写错误mod文件操作{usestd::fs;usestd::io;pubfn读取文件(路径:str)-ResultString,io::Error{fs::read_to_string(路径)}pubfn写入文件(路径:str,内容:str)-Result(),io::Error{fs::write(路径,内容)}}fnmain(){use文件操作::{读取文件,写入文件};// 写入文件match写入文件(测试.txt,你好世界){Ok(_)println!(文件写入成功),Err(错误)println!(写入错误{},错误),}// 读取文件match读取文件(测试.txt){Ok(内容)println!(文件内容{},内容),Err(错误)println!(读取错误{},错误),}// 读取不存在的文件match读取文件(不存在的文件.txt){Ok(内容)println!(文件内容{},内容),Err(错误)println!(读取错误{},错误),}}创建计算器模块处理各种计算错误mod计算器{#[derive(Debug)]pubenum计算错误{除数为零,负数平方根,}pubfn除法(分子:f64,分母:f64)-Resultf64,计算错误{if分母0.0{Err(计算错误::除数为零)}else{Ok(分子/分母)}}pubfn平方根(数字:f64)-Resultf64,计算错误{if数字0.0{Err(计算错误::负数平方根)}else{Ok(数字.sqrt())}}}fnmain(){use计算器::{除法,平方根,计算错误};// 测试除法match除法(10.0,2.0){Ok(结果)println!(10.0 / 2.0 {},结果),Err(错误)println!(除法错误{:?},错误),}match除法(10.0,0.0){Ok(结果)println!(10.0 / 0.0 {},结果),Err(错误)println!(除法错误{:?},错误),}// 测试平方根match平方根(9.0){Ok(结果)println!(√9.0 {},结果),Err(错误)println!(平方根错误{:?},错误),}match平方根(-1.0){Ok(结果)println!(√-1.0 {},结果),Err(错误)println!(平方根错误{:?},错误),}}进阶练习选做使用?操作符处理错误mod文件操作{usestd::fs;usestd::io;pubfn读取并处理文件(路径:str)-ResultString,io::Error{let内容fs::read_to_string(路径)?;// 错误传播let处理后的内容内容.to_uppercase();Ok(处理后的内容)}}fnmain(){use文件操作::读取并处理文件;match读取并处理文件(测试.txt){Ok(内容)println!(处理后的内容{},内容),Err(错误)println!(错误{},错误),}}创建嵌套模块mod工具包{pubmod数学工具{pubfn加法(a:i32,b:i32)-i32{ab}pubfn减法(a:i32,b:i32)-i32{a-b}}pubmod字符串工具{pubfn反转(文本:str)-String{文本.chars().rev().collect()}pubfn统计字数(文本:str)-usize{文本.chars().count()}}}fnmain(){use工具包::数学工具::{加法,减法};use工具包::字符串工具::{反转,统计字数};println!(1 2 {},加法(1,2));println!(5 - 3 {},减法(5,3));println!(反转{},反转(你好));println!(字数{},统计字数(你好世界));}挑战练习拓展创建一个完整的模块系统// 主程序mod学生管理{pubmod数据{pubstruct学生{pub姓名:String,pub年龄:i32,pub成绩:i32,}}pubmod操作{usesuper::数据::学生;pubfn添加学生(学生们:mutVec学生,学生:学生){学生们.push(学生);}pubfn计算平均成绩(学生们:Vec学生)-f64{if学生们.is_empty(){return0.0;}letmut总分0;for学生in学生们{总分学生.成绩;}总分asf64/学生们.len()asf64}}}fnmain(){use学生管理::数据::学生;use学生管理::操作::{添加学生,计算平均成绩};letmut学生们vec![];添加学生(mut学生们,学生{姓名:String::from(张三),年龄:15,成绩:95});添加学生(mut学生们,学生{姓名:String::from(李四),年龄:14,成绩:88});添加学生(mut学生们,学生{姓名:String::from(王五),年龄:15,成绩:92});println!(学生列表);for学生in学生们{println!({}{}岁{}分,学生.姓名,学生.年龄,学生.成绩);}let平均分计算平均成绩(学生们);println!(平均成绩{}分,平均分);}4. 知识总结核心概念回顾模块组织代码的方式将相关的代码放在一起访问控制使用pub关键字控制模块项的可见性模块嵌套模块可以嵌套形成层次结构use关键字简化模块的使用错误处理处理程序运行时的错误Result类型表示可能失败的操作?操作符简化错误传播自定义错误类型定义自己的错误类型panic!宏处理不可恢复的错误关键代码速查功能代码说明模块定义mod 模块名 { 代码 }定义一个模块公共项pub fn 函数名() {}定义一个公共函数模块嵌套mod 模块名 { mod 子模块 { 代码 } }定义嵌套模块使用模块use 模块名::项;简化模块的使用Result类型fn 函数() - ResultT, E { 代码 }定义可能失败的函数错误传播let 结果 可能失败的操作()?;自动传播错误错误处理match 结果 { Ok(值) 处理成功, Err(错误) 处理错误 }处理Result类型常见错误提醒访问控制忘记使用pub关键字导致外部无法访问模块项模块路径模块路径错误导致无法找到模块项错误处理忘记处理Result类型导致编译错误?操作符在非Result返回类型的函数中使用?操作符panic!过度使用panic!应该使用Result类型处理可恢复的错误5. 课后作业巩固练习题编写一个程序创建一个数学工具模块包含加减乘除函数编写一个程序创建一个字符串工具模块包含字符串处理函数编写一个程序使用Result类型处理文件读写错误创意编程题发挥你的创意创建一个完整的模块系统包含多个子模块编写一个程序实现一个简单的错误处理系统处理各种可能的错误下篇预习提示下一篇我们将学习综合实战——制作我的小游戏这是一个综合运用所学知识的项目。预习一下如何选择一个适合的项目如何设计项目的结构如何实现项目的功能如何测试和调试项目恭喜你完成了第十二篇教程你已经掌握了模块系统和错误处理学会了如何组织代码和处理问题。继续加油下一篇我们将进行综合实战制作一个完整的小游戏学习交流如果你在学习过程中遇到任何问题欢迎联系我** 视频号“时空系”邮箱wbtm2718qq.com其他提示本人正在开发纯中文编程语言和编译器有兴趣的可以交流本人正在写一本基于中国母语思维习惯的数学教材可以让你一年内学完从初中到研究生的数学而且可以让你快速掌握人工智能的关键数学知识有兴趣的可以交流祝你学习愉快