包管理 Rust的包管理有三个重要的概念,分别是箱、包、模块
箱(Crate) 这个Crate是二进制程序文件或者是苦文件,存在于包中
是树状结构,树根是编译器开始运行时编译的源文件所编译的程序
二进制程序文件不一定是可执行文件,只能确定是包含沐白哦机器语言的文件,文件格式主要取决于编译环境
包(Package) 我们使用cargo new
一个Rust工程时,这个工程其实就是一个包,包必须由Cargo.toml
文件来管理,主要是描述包的基本信息和依赖项
一个包最多包含一个库Crate,但是可以包含任意二进制Crate,但是至少包含一个Crate
当我们创建完项目之后,会有一个main.rs,这其实就意味着这是一个二进制项目
模块(Module) Rust中组织的单位是模块,模块有很多中声明方式
例如
1 2 3 4 5 6 7 8 9 10 11 mod nation { mod government { fn govern () {} } mod congress { fn legislate () {} } mod court { fn judicial () {} } }
用树形图表示就是
1 2 3 4 5 6 7 nation ├── government │ └── govern ├── congress │ └── legislate └── court └── judicial
Rust中描述目录结构是使用::
的
从根目录(crate
)开始的绝对路径是crate::nation::government::govern();
,也可以使用相对路径nation::government::govern();
访问权限 Rust中只有两种访问权限,public
和private
默认是private
的,要设置为公有,则需要设置pub
关键字,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 mod nation { pub mod government { pub fn govern () {} } mod congress { pub fn legislate () {} } mod court { fn judicial () { super::congress::legislate (); } } } fn main () { nation::government::govern (); }
对于结构体来说,结构体本身也可以加上pub,能让外部访问,但是其字段依旧是私有的,如果想要访问,也需要pub来声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 mod back_of_house { pub struct Breakfast { pub toast: String , seasonal_fruit: String , } impl Breakfast { pub fn summer (toast: &str ) -> Breakfast { Breakfast { toast: String ::from (toast), seasonal_fruit: String ::from ("peaches" ), } } } } pub fn eat_at_restaurant () { let mut meal = back_of_house::Breakfast::summer ("Rye" ); meal.toast = String ::from ("Wheat" ); println! ("I'd like {} toast please" , meal.toast); } fn main () { eat_at_restaurant () }
在Rust中,每一个文件其实就相当于一个模块
use关键字 use关键字其实就相当于一种简便写法,可以将模块中的函数导入到当前文件,也可以使用as起一个别名
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 mod nation { pub mod government { pub fn govern () {} } pub fn govern () {} } use crate::nation::government::govern;use crate::nation::govern as nation_govern;fn main () { nation_govern (); govern (); }
rust也自带一些标准库,可以直接使用
rust标准库
错误处理 Rust中有一套独特的处理异常情况的机制,和C++中的try还是有很大区别的
Rust的错误有两种,可恢复错误和不可恢复错误
可恢复错误,例如说文件读写错误,这个文件有可能被其他程序占用,这是正常的,我们可以通过等待来解决这个问题
但是还有一种说逻辑错误导致的,例如数组越界访问,在C++中array是通过异常来表示的
Rust中则没有异常,对于这两种错误是有不同的处理方法的
不可恢复错误 这里我们需要使用宏,对于宏我们还是后面会具体讲,这里只是会用就行
我们来看一个宏函数panic!
1 2 3 4 fn main () { panic! ("error occured" ); println! ("Hello, Rust" ); }
运行之后我们会发现,没有输出Hello, Rust
, 而是在调用panic!
之后就停止运行了
可恢复错误 在C/C++中,我们通常用整数做返回值来表达函数遇到的错误,但是Rust是通过一个枚举表达的
1 2 3 4 enum Result <T, E> { Ok (T), Err (E), }
在标准库中,可能产生异常的函数的返回值都是Result枚举类型的
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 use std::fs::File;fn main () { let f = File::open ("hello.txt" ); match f { Ok (file) => { println! ("File opened successfully." ); }, Err (err) => { println! ("Failed to open the file." ); } } }
如果用if let
会更简单一些
1 2 3 4 5 6 7 8 9 10 use std::fs::File;fn main () { let f = File::open ("hello.txt" ); if let Ok (file) = f { println! ("File opened successfully." ); } else { println! ("Failed to open the file." ); } }
如果想要一个可恢复错误按照不可恢复错误来处理,就可以使用两个方法
unwarp()
expect(message: &str)
例如
1 2 3 4 5 6 use std::fs::File;fn main () { let f1 = File::open ("hello.txt" ).unwrap (); let f2 = File::open ("hello.txt" ).expect ("Failed to open." ); }
这种就相当于在枚举是Err时调用panic!宏,区别就是expect可以发信息
错误传递 我们可以直接使用Err(value)
作为枚举来传递错误,这里的value
是泛型,可以传递任意类型的参数
泛型我们下一篇来详细讲
如果我们如果想要传递泛型,接收端想要再传递出去,那就得写一个match来判断
例如这样
1 2 3 4 5 6 7 fn g (i: i32 ) -> Result <i32 , bool > { let t = f (i); return match t { Ok (i) => Ok (i), Err (b) => Err (b) }; }
然后Rust又给了一个语法糖,可以在返回值是Result枚举的情况下在函数末尾加一个?
,就可以直接返回错误类型了
1 2 3 4 fn g (i: i32 ) -> Result <i32 , bool > { let t = f (i)?; Ok (t) }
这里要注意类型的统一
kind方法 Rust中好像没有类似try一样,可以把同一类型的错误全部放在一个相同的解决处理的能力
但是我们可以把处理异常单独写一个函数,然后使用异常里的kind
方法,就能知道Result的Err类型了,然后就可以针对问题进行解决
例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 use std::io;use std::io::Read;use std::fs::File;fn read_text_from_file (path: &str ) -> Result <String , io::Error> { let mut f = File::open (path)?; let mut s = String ::new (); f.read_to_string (&mut s)?; Ok (s) } fn main () { let str_file = read_text_from_file ("hello.txt" ); match str_file { Ok (s) => println! ("{}" , s), Err (e) => { match e.kind () { io::ErrorKind::NotFound => { println! ("No such file" ); }, _ => { println! ("Cannot read the file" ); } } } } }