Rust包管理与错误处理

文章目录

包管理

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中只有两种访问权限,publicprivate

默认是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标准库

错误处理

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");
                }
            }
        }
    }
}
相关推荐
楼兰公子4 小时前
buildroot 在编译rust时裁剪平台类型数量的方法
开发语言·后端·rust
Rust研习社10 小时前
开源项目里的 deny.toml 是什么?
后端·rust·编程语言
铭毅天下16 小时前
当搜索引擎遇上 Rust——深度解读下一代实时搜索引擎 INFINI Pizza
开发语言·后端·搜索引擎·rust
咸甜适中16 小时前
rust语言学习笔记Trait之Default(默认值)
笔记·学习·rust
容智信息1 天前
AI Agent(智能体)的输出格式应该从 Markdown 转向 HTML吗?
前端·人工智能·rust·编辑器·html·prompt
Rust研习社2 天前
Rust Clippy 实用指南:写出更优雅、安全的 Rust 代码
后端·rust·编程语言
yangyongdehao302 天前
两天用AI+rust撸了一款本地批量去水印软件,30MB,效果能打
ai作画·rust
nudt_qxx2 天前
NVIDIA 正式开源cuda-oxide!Rust 编写 CUDA 内核新范式!
rust
小杍随笔3 天前
【Rust桌面革命:Tauri×Dioxus——架构对决、实战拆解与2026选型杀招】
开发语言·架构·rust