文章目录
包管理
Rust的包管理有三个重要的概念,分别是箱、包、模块
箱(Crate)
这个Crate是二进制程序文件或者是苦文件,存在于包中
是树状结构,树根是编译器开始运行时编译的源文件所编译的程序
二进制程序文件不一定是可执行文件,只能确定是包含沐白哦机器语言的文件,文件格式主要取决于编译环境
包(Package)
我们使用cargo new
一个Rust工程时,这个工程其实就是一个包,包必须由Cargo.toml
文件来管理,主要是描述包的基本信息和依赖项
一个包最多包含一个库Crate,但是可以包含任意二进制Crate,但是至少包含一个Crate
当我们创建完项目之后,会有一个main.rs,这其实就意味着这是一个二进制项目
模块(Module)
Rust中组织的单位是模块,模块有很多中声明方式
例如
rust
mod nation {
mod government {
fn govern() {}
}
mod congress {
fn legislate() {}
}
mod court {
fn judicial() {}
}
}
用树形图表示就是
shell
nation
├── government
│ └── govern
├── congress
│ └── legislate
└── court
└── judicial
Rust中描述目录结构是使用::
的
从根目录(crate
)开始的绝对路径是crate::nation::government::govern();
,也可以使用相对路径nation::government::govern();
访问权限
Rust中只有两种访问权限,public
和private
默认是private
的,要设置为公有,则需要设置pub
关键字,例如
rust
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来声明
rust
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起一个别名
例如
rust
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中有一套独特的处理异常情况的机制,和C++中的try还是有很大区别的
Rust的错误有两种,可恢复错误和不可恢复错误
可恢复错误,例如说文件读写错误,这个文件有可能被其他程序占用,这是正常的,我们可以通过等待来解决这个问题
但是还有一种说逻辑错误导致的,例如数组越界访问,在C++中array是通过异常来表示的
Rust中则没有异常,对于这两种错误是有不同的处理方法的
不可恢复错误
这里我们需要使用宏,对于宏我们还是后面会具体讲,这里只是会用就行
我们来看一个宏函数panic!
rust
fn main() {
panic!("error occured");
println!("Hello, Rust");
}
运行之后我们会发现,没有输出Hello, Rust
, 而是在调用panic!
之后就停止运行了
可恢复错误
在C/C++中,我们通常用整数做返回值来表达函数遇到的错误,但是Rust是通过一个枚举表达的
rust
enum Result<T, E> {
Ok(T),
Err(E),
}
在标准库中,可能产生异常的函数的返回值都是Result枚举类型的
例如
rust
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
会更简单一些
rust
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)
例如
rust
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来判断
例如这样
rust
fn g(i: i32) -> Result<i32, bool> {
let t = f(i);
return match t {
Ok(i) => Ok(i),
Err(b) => Err(b)
};
}
然后Rust又给了一个语法糖,可以在返回值是Result枚举的情况下在函数末尾加一个?
,就可以直接返回错误类型了
rust
fn g(i: i32) -> Result<i32, bool> {
let t = f(i)?;
Ok(t)
}
这里要注意类型的统一
kind方法
Rust中好像没有类似try一样,可以把同一类型的错误全部放在一个相同的解决处理的能力
但是我们可以把处理异常单独写一个函数,然后使用异常里的kind
方法,就能知道Result的Err类型了,然后就可以针对问题进行解决
例如
rust
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");
}
}
}
}
}