Rust能力养成之(9):Cargo and Crates项目管理(下)

图片

 

前言

 

在上一篇,我们讲了

  • Cargo项目创建

  • Cargo和依赖关系

 

在本篇中,会涉及

  • Cargo运行测试

  • Cargo运行实例

  • Cargo工作空间

 

Cargo运行测试

 

如题,Cargo支持运行测试和基准测试,有关这方面的深度介绍,还要放在下一章。

在本节中,我们将简要介绍如何使用Cargo运行测试。这里将为一个库编写测试,那么首先,用cargo new myexponent--lib来创建一个库。

 

可见:

图片

库(library)的crate类似于二进制crate,不同的是,这里使用src/lib.rs以及一个简单的测试函数it_works(该函数用#[test]注释标记),替代src/main.rs和内部的主函数入口点。我们可以使用cargo测试,立即运行it_works测试函数,并查看它是否通过。

略微说明一下,一般称crate为库或者包,在Rust中,有library的crate,也有二进制的crate,希望读者不要被翻译中带来的麻烦所迷惑。

图片

结果显示通过。

现在,让我们用Cargo进行测试驱动开发(Test Driven Development,TDD);在这个库里加一个指数函数pow function;为这个函数编写一个测试,最初测试失败,然后增加实现代码,直到能通过。

 

以下是src/lib.rs文件,已经有了pow函数,但没有任何实现:​​​​​​

// myexponent/src/lib.rsfn pow(base: i64, exponent: usize) -> i64 {     unimplemented!();
#[cfg(test)] mod tests {     use super::pow;     #[test]     fn minus_two_raised_three_is_minus_eight() {         assert_eq!(pow(-2, 3), -8);     }}

这里创建了一个单独的pow函数,在mod tests内部,有一个名为minus_two_raised_three_is_minus_eight的测试函数,执行一个判断两边是否相等的宏assert_eq !。

如果我们运行cargo test, pow调用的单元测试显然会失败,因为这里有一个unimplemented!() 宏调用

图片

简言之,unimplemented!() 是一个很方便的宏,可以用来标记未完成或稍后运行的代码,但要通过编译。而在运行内部,会调用一个panic!的宏,返回尚未实现的信息"not yet implemented",可以用于实现trait(一般翻译为特性,是Rust中很重要的概念)的多种方法。

 

例如,你开始实现一个方法,但没有计划实现其他方法。在编译时,如果只将未实现的方法函数置为空的函数体,那么会出现编译错误。

对于这些方法,可以在内部放置一个unimplemented!() 宏调用,以保证通过编译和运行无误。

 

我们对这个代码潦草的修改一下:

// myexponent/src/lib.rs
pub fn pow(base: i64, exponent: usize) -> i64 {    let mut res = 1;    if exponent == 0 {        return 1;    }    for _ in 0..exponent {        res *= base as i64;    }    res}
#[cfg(test)]mod tests {    use super::pow;    #[test]    fn minus_two_raised_three_is_minus_eight() {        assert_eq!(pow(-2, 3), -8);    }}显然通过测试了,这还仅仅是基础,一切才刚刚开始。

图片

 

Cargo运行实例

为了让用户可以快速上手你写的crate,最好给些例子。Cargo将此列为必要元素,也就是在root目录下可以添加一个名为examples的文件夹,里面可以写需要的各种实例文件。

图片

那么我们先写一个:​​​​​​​

use myexponent::pow;
fn main() {    println!("8 raised to 2 is {}", pow(8, 2));}

 

在该代码导入了myexponent::pow,并调用。结果如下,有了这些,相信读者应该可以创建一下简单的东西了。

图片

Cargo工作空间

随着时间的推移,项目内容会变得相当庞大。现在,将代码的公共部分分割为单独的crate,有助于高效的项目管理。Cargo工作空间就是做这个的,其可以保证在本地的目录中拥有可以共享相同Cargo.lock文件,和一个通用的目标或输出目录。

 

这里,我们将创建一个合并Cargo工作空间的新项目,该工作空间只是一个包含Cargo.toml的目录。它没有任何[package]部分,但有一个[workspace]部分在其中。

我们创建一个名为workspace_demo的新目录,并添加Cargo.toml文件​​​​​​​

# worspace_demo/Cargo.toml
[workspace]members = ["my_crate", "app"]

图片

在[workspace]中,members键是workspace目录下的crate列表。现在,在workspace_demo目录中,我们将创建两个crate:my_crate和app。为了简单起见,在my_crate中设置一个公共API,只打印一条欢迎消息。​​​​​​​

// workspace_demo/my_crate/src/lib.rs
pub fn greet() {    println!("Hi from my_crate");}

在app crate中,写一个main函数,来调用my_crate中的函数。​​​​​​​

// workspace_demo/app/src/main.rs
fn main() { my_crate::greet();}

 

还没有完,我们需要让Cargo知道我们的my_crate依赖项。由于my_crate是一个本地crate,需要在app的Cargo.toml文件中指定依赖项:​​​​​​​

# workspace_demo/app/Cargo.toml
[package]name = "app"version = "0.1.0"authors = ["wuciren"]edition = "2018"
[dependencies]my_crate = { path = "../my_crate" }

 

现在,当我们运行cargo build时,二进制文件将在workspace_demo目录的目标目录中生成。相应的,我们可以在workspace_demo目录中添加多个本地crate。如果想要添加来自crates.io的第三方依赖项,需要把放到所有对其有需要的crate种。然而,在构建过程中,Cargo确保在cargo.lock中只有该依赖项的单一版本,这确保了第三方依赖项不会被重新构建和复制。

 

 

结语

 

内容至此,读者应该有所感觉,好像可以自己构建一些软件包的壳子了,这一点直觉是正确的,虽然还不够,但已经稳步踏在将用Rust代码实现想法的路上了。

下一篇,我们会讲一下如何使用外部的工具来扩展和增强现有的内容和形式

 

主要参考和建议读者进一步阅读的文献

https://doc.rust-lang.org/book

1.Rust编程之道,2019, 张汉东

2.The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger

3.Hands-On Data Structures and Algorithms with Rust,2018,Claus Matzinger

4.Beginning Rust ,2018,Carlo Milanesi

5.Rust Cookbook,2017,Vigneshwer Dhinakaran

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值