坏蛋Dan
知乎@坏蛋Dan
发布时间:2024.1.4

前言

昨天我们用iterator优化了我们的minigrep小工具

坏蛋Dan:rust基础学习--day31

今天往下学~


深入的了解Cargo以及Crates.io

目前我们仅是会一些基础的指令,比如run/build/test,我们将在这一章学到一些较深入的cargo相关的知识。

比如:

  1. 通过发布配置文件(release profiles)来自定义构建。
  2. 发布一个库到crates.io中。
  3. 使用工作区(workspaces)来组织大型项目。
  4. crates.io中下载库。
  5. 基于cargo拓展自定义的指令。

Cargo的官方链接:

Introduction - The Cargo Bookdoc.rust-lang.org/cargo/

通过配置文件自定义构建

release profiles是需要在构建之前配置的,我们可以配置多个文件来使用不同的配置项.

比如前端工程化有devprod两种模式.

而在rust中也差不多,有devrelease两种.

当我们运行cargo build的时候就是dev模式,而执行cargo build --release则是release模式.

这俩模式其实cargo已经有一套较好的默认值了,这也就是为什么我们不需要配置cargo.toml就可以执行上面两个指令的原因.

可以看到构建输出的内容是会展示是dev还是release的.

现在我们来看下如何单独配置它们的项

首先找到我们的Cargo.toml文件,所有的配置都是放在这个文件中的,而要单独配置某种mode只需要单独开个sections也就是区域就行了,比如下面这样

默认配置的opt-level也是这样的

opt-level的范围是0 ~ 3,表示编译器准备采取什么等级来优化代码.

等级越高则相对的编译需要的时间就越长,毕竟需要优化的东西更多.

开发模式我们只是看下效果,并不需要考虑优化之类的,所以给个0就好.

而发布模式由于代码是准备上线了,上线后也不会怎么去改动了,所以只需要编译一次,能优化自然就优化掉.

当然这只是推荐的,如果你想在开发模式就看到最终效果,那你也可以把profile.devopt-level改为3.

就介绍到这里,想要什么配置可以看下官方提供的文档

Profiles - The Cargo Bookdoc.rust-lang.org/cargo/reference/profiles.html


发布一个cratecrates.io

和我们发布库到npm挺像的.

不过在发布前,我们得有要发布的东西才行.

我们先创建一个项目,就放一些用于计算的方法吧

lib.rs

然后我们来测试下

也是正常之后我们就可以准备给我们的代码加上注释了

添加文档注释

一个库中的代码应该是得有文档注释的,比如标准库里的

args方法为例子,可以看到有一堆的注释,而这些注释是用///三斜杠来标注的.

rust将他们称为: 文档注释,它们可以帮助是用这些个方法的开发者知道这个方法要怎么用,是干嘛的.

而这些注释里的例子也就是cargo test时出现在最底部的那个

是的,注释里的东西也是可以被test感知的.

我们直接来试一下

然后我们可以运行cargo doc --open将文档以html的形式输出到浏览器中

是不是非常的方便~

实际上cargo是创建了一个target/doc的文件夹

这个文档注释是支持markdown的语法的,所以为了好看些可以加一些markdown的东西,来调整下结构.

另外官方还提供了几个sections用来区分场景,比如我们前面写的# Exmaples

  • # Panics: 这个方法可能会引起panic,如果你想确保你的代码不会因为这个方法而panic,那么请在xxx场景下不要使用该方法
  • # Errors: 这个方法返回的是一个Result的类型,而这个E在什么场景下是什么类型,什么情况下会出现
  • # Safety: 如果这个方法是unsafe的,那么就需要说清楚为什么是不安全的,以及提供替代方案.

大部分情况下我们都不会使用,不过有需要时还是尽量加上,因为对于用户来说体验感会好很多

另外我们还可以用//!在文件头注释这个crate里有哪些module,毕竟在入口就有指引是一件很舒服的事情.

不过需要注意//!注释得和代码之间隔一个空行.


重写导出路径

先不说rust,说一个开发库的常态.

以前端为例子

我们开发者开发一个库,为了可维护性,我们会把这个库里的文件按各种不同点拆分,拆分成十几个文件等.

这对于开发者来说可维护性就上去了, 但是对于用代码的人来说就是个灾难: 入口有好几个, 方法一个套一个.

这也就是为什么大多数库都以一个index文件作为入口, 其实都是为了使用者可以方便的使用.

他们一般会把各种入口都importindex文件中,然后export出去.

回到rust中,想一下如果不这么做我们要怎么写,大概率会是这样

lib.rs

main.rs

很冗长,所以rust团队提供了重导的方式,关键词pub use

lib.rs

main.rs

这样就简写了很多.

另外这一点也是会暴露在文档里的

我们可以直接点击c进入c这个mod中.


配置Cargo.toml文件

和配置package.json差不多,在发布前,我们得配置下这个库的名字作者以及开源协议等.

具体有哪些metadata可以看这里: Introduction - The Cargo Book


创建账号

既然我们代码有了,注释也有了,配置文件也配置完了并且测试正常,那这个时候就可以准备发布了.

不过在这之前,我们得有一个自己的账号.

Rust Package Registry

我们用github账号登录即可.

然后配置下自己的邮箱

https://crates.io/settings/email-notifications

验证完邮箱之后就可以拿我们的token

https://crates.io/settings/tokens

拿到token之后我们到终端登陆下

当然,如果你的crates.io没有更新会先帮你更新,记得换源或者登梯子.


发布到crates.io

登录成功后我们还是不要急着发布.

我们先到crates.io官网看下我们的包是否重名了

并没有,那么就可以放心发布了

不过我还是遇到了一个错误

我的代码中有改动没有同步到git上,但是我这个项目压根没有git仓库,所以我直接--allow-dirty跳过了

然后到官网搜索一下

这样就成功啦~


更新库

如果到时候需要更新,直接改下Cargo.toml里的version然后重新cargo publish即可.


废弃库

rust并不允许我们直接把库给废弃删除,我们只能废弃之前的版本,这样其它开发者就不会去使用这个版本.

我们直接废弃到我们目前的calc_tool,由于这个包只是个demo,所以并不打算要.

可以看到被0.1.0废弃了

如果反悔了,可以在终端输入

这样就会恢复了


Cargo Workspaces

一些大型的框架都会有多个子包比如vuecore[12]包,这是一种国外很火的概念,叫monorepo,就是多包管理.前端可以使用yarn + lerna[13]或者pnpm[14]来管理.

而在rust中,Cargo提供了管理多包的方式,叫workspaces.

创建一个workspaces

我们先创建一个add文件夹

然后我们在add文件夹中直接创建一个Cargo.toml文件

这个Cargo.toml将作用于整个包

然后我们创建一个member

创建完之后我们直接终端输入cargo build

可以看到adder里并没有target,即使是跑到adder里面去执行build指令也是只有add里有target.

为什么要统一输出呢?因为这些个包之间大概率是存在关联引用关系的,如果每个包里都有自己的target,那么就有可能存在重复打包的问题.


workspace中创建第二个包

先改下add/Cargo.toml

然后终端

我们创建了一个library crate

现在的结构变成了这样

然后我们给add_one加点料之后在adder里面引用它

接着要在adder里实现引用它

我们先去到adder/Cargo.toml中,add_one此时对于adder来说就是一个library crate,和通过crates.io引用的其实没什么差别

然后我们就可以在main.rs中使用add_one这个方法了(当然得先use)

终端正常输出

当然,如果你只想输出adder这个binary crate包的内容,可以使用cargo run -p adder,用p指令来指定某一个binary crates编译运行.


引入外部包

还记得我们的目录么?

我们只有一个Cargo.lock文件,这也就是意味着所有外部引入的包都会共用同一个版本.

这样做可以避免多个版本带来的兼容性问题.

我们来试下把rand这个crate引入到add_one或者adder里面

然后我们在文件中使用

接着cargo run一下

也是没问题,来看下Cargo.lock文件中的记录

不过有一点需要注意,那就是只有在其中一个包中引入了才能使用这个包.

比如我们在adder的包中引入了,但是没有在add_one中引入,这时如果在add_one中直接使用就会报错.

所以如果想在add_one中使用,那么也需要在add_one/Cargo.toml文件中引入才行.

至于直接加在全局的Cargo.toml这样的操作是不行的,会报错.


添加测试代码

其实add_one/lib.rs已经有测试代码了,毕竟我们是new add_one --lib的方式创建的.

那么我们直接cargo test,它将把所有包中的测试代码都执行一遍.

当然,如果你只是想测试某个包中的测试代码,也可以用p指令指明是哪个包,这里就不演示了,和上面cargo run -p xxx的用法一致.

这个指令同样适用于发布creates的时候,需要注意发布的时候要每个包单独发布才行.


使用install指令下载binary crates

库我们试过了,准确来说是依赖,我们可以通过Cargo.toml文件中[dependencies]引入.

binary crates我们就没遇到过了.

其实binary crates并不是用在我们的代码中的.

比如ripgrep[20],它只是一个工具,可以用来匹配而已.

我们来下载下,下载完之后默认是放到user/.cargo/bin里面

可以看到是正常下载了.


基于Cargo拓展自定义指令

其实和npx一样,你可以直接通过package.json中配置bin选项来自定义指令.

官方没有给个例子....

所以这里只能简单的说下

如果你通过install下载的binary crates命名是cargo-xxx,那么你就可以直接在终端cargo xxx跑这个指令调动这个binary.

比如

然后我们直接cargo --list查找当前可用指令,发现这个cargo-miri可以直接使用cargo miri来调用.


总结

今天的量也是挺多的,不过并不是晦涩难懂的特性概念等.

发布一个自己的包还是很有趣很有成就的.

参考

  1. ^rust-14-more-about-cargo-and-crates.io https://doc.rust-lang.org/book/ch14-00-more-about-cargo.html
  2. ^rust-14.1-customizing-builds-with-release-profiles https://doc.rust-lang.org/book/ch14-01-release-profiles.html#customizing-builds-with-release-profiles
  3. ^rust-publish-a-crate-to-crates.io https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#publishing-a-crate-to-cratesio
  4. ^rust-make-useful-document-comment https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#making-useful-documentation-comments
  5. ^rust-14.2-exporting-a-convenient-public-api-with-pub-use https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#exporting-a-convenient-public-api-with-pub-use
  6. ^rust-adding-metadata-to-a-new-crate https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#adding-metadata-to-a-new-crate
  7. ^rust-setting-up-crates.io-account https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#setting-up-a-cratesio-account
  8. ^rust-publishing-to-crates.io https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#publishing-to-cratesio
  9. ^rust-updating-crates https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#publishing-a-new-version-of-an-existing-crate
  10. ^rust-deprecating-version https://doc.rust-lang.org/book/ch14-02-publishing-to-crates-io.html#deprecating-versions-from-cratesio-with-cargo-yank
  11. ^rust-Cargo-Workspaces https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html#cargo-workspaces
  12. ^vue-core https://github.com/vuejs/core
  13. ^lerna http://www.febeacon.com/lerna-docs-zh-cn/routes/basic/concepts.html
  14. ^pnpm https://github.com/pnpm/pnpm
  15. ^rust-creating-a-workspaces https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html#creating-a-workspace
  16. ^rust-creating-second-crates-in-workspace https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html#creating-the-second-package-in-the-workspace
  17. ^rust-depending-on-external-package-in-workspace https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html#depending-on-an-external-package-in-a-workspace
  18. ^rust-adding-test-to-workspace https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html#adding-a-test-to-a-workspace
  19. ^rust-install-binary-with-cargo-install https://doc.rust-lang.org/book/ch14-04-installing-binaries.html
  20. ^rust-ripgrep https://crates.io/crates/ripgrep
  21. ^rust-extending-cargo-with-custom-commands https://doc.rust-lang.org/book/ch14-05-extending-cargo.html#extending-cargo-with-custom-commands

发布于 2022-12-29 18:01・IP 属地广东