26_rust_cargo、crates.io
rust_cargo、crates.io
通过release profile自定义构建
release profile(发布配置):是预定义的,可自定义,可使用不用的配置,对代码编译拥有更多的控制。
每个profile的配置都独立于其它的profile。
cargo主要的两个profile:
- dev profile:适用于开发,cargo build
- release profile:适用于发布,cargo build -release
自定义profile
针对每个profile,cargo都提供了默认的配置,如果想自定义xxx profile配置,可在cargo.toml里添加[profile.xxx]
区域,在里面覆盖默认配置的子集。
[profile.dev] opt-level = 0 # 优化等级为0,O0优化 [profile.release] opt-level = 3
对于每个配置的默认值和完整选项, 请参见: https://doc.rust-lang.org/cargo/
发布create到crates.io
crates.io是提供依赖库的,自己开发的crate也可通过发布包来共享代码。crate的注册表在https://crates.io/:
它会分发已注册的包的源代码,主要托管开源的代码。
创建并设置Crates.io账号
1)发布crate前,需在crates.io创建账号并获得API token,可用github账号登录,
2)运行命令:cargo login [你的API token]
,通知cargo,你的API token存储在本地~/.cargo/credentials
注:API token可在https://crates.io/进行撤销
为新的crate添加元数据
在发布crate前,需要在Cargo.toml的package]
区域为crate添加一些元数据:
- crate需要唯一的名称:name
- description;一两句描述,后会出现在crate搜索的结果里
- license:需提供许可标识值(可到http://spdx.org/licenses/ 查找),可指定多个license,用OR隔开
- version
- author
发布到Crates.io
然后发布:cargo publish命令
crate一旦发布,就是永久性的,该版本无法覆盖,代码无法删除,目的是依赖于该版本的项目可继续正常工作。
发布已存在的crate新版本
修改crate后,需要先修改Cargo.toml里的version值,再重新发布。参照http://semver.org/ 使用你的语义版本。再执行cargo publish命令发布。
使用cargo yank撤回版本
使用cargo yank从Crates.io撤回版本:
- 不可删除crate之前的版本
- 但可防止其它项目把它作为新的依赖,yank(撤回)一个crate版本后,可防止新项目依赖于该版本,已存在项目可继续使用和依赖(并可下载)
- yank意味着所有已产生的Cargo.lock的项目都不会中断,任何将来生成的Cargo.lock文件都不会使用被yank的版本。
- 命令:yank一个版本,不会删除任何代码,cargo yank --vers 1.0.1
- 取消yank:cargo yank --vers 1.0.1 --undo
文档注释
在函数或方法前,添加文档注释,用于生成帮助文档:
- 生成HTML文档
- 显示公共API的文档注释,如何使用API
- 使用
///
来注释 - 支持Markdown语法
- 放置在被说明条目之前
生成HTML文档的命令
cargo doc
命令
- 它会运行rustdoc工具(rust安装包时自带的工具)
- 把生成的HTML文档放在target/doc目录下
cargo doc --open
命令
- 构建当前crate的文档(也包含crate依赖项的文档)
- 并在浏览器中打开文档
例子:创建lib.rs文件,内容:
/// add function api /// # Example /// ```rust /// let a = 5; /// let b = 6; /// let ret = hello_cargo::add_func(a, b); /// assert_eq!(11, ret); /// ``` pub fn add_func(a: i32, b: i32) -> i32 { a + b }
执行cargo doc --open即可打开文档
常用章节
比如代码中的# Examples
章节,其他常用章节:
- Panics:函数可能发生panic的场景
- Errors:如果函数返回Result,描述可能的错误种类,以及可导致错误的条件
- Safety:如果函数处于unsafe调用,应解释函数unsafe的原因,以及调用者确保的使用前提
文档注释作为测试
示例代码块的附加值:运行cargo test的时候,会把文档注释中的示例代码作为测试来运行
如创建hello_cargo项目,创建src/lib.rs文件,内容:
/// add function api /// # Example /// ``` /// let a = 5; /// let b = 6; /// let ret = hello_cargo::add_func(a, b); /// assert_eq!(11, ret); /// ``` pub fn add_func(a: i32, b: i32) -> i32 { a + b }
执行cargo test命令,打印:
Doc-tests hello_cargo running 1 test test src\lib.rs - add_func (line 3) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.32s
为包含注释的项添加文档注释
使用符号://!
,这类注释通常用于描述crate和模块,crate root(按惯例src/lib.rs),或者一个模块内,将crate或模块作为一个整体进行记录。
//! # hello_cargo crate //! //! `hello_cargo` is test crate //! /// add function api /// # Example /// ``` /// let a = 5; /// let b = 6; /// let ret = hello_cargo::add_func(a, b); /// assert_eq!(11, ret); /// ``` pub fn add_func(a: i32, b: i32) -> i32 { a + b }
再次执行cargo doc --open后,打开的文档最上方多出了内容:
hello_cargo crate hello_cargo is test crate
pub use
使用pub use导出方便使用的公共API。
在实际项目中,会存在如下的问题:
crate的程序结构在开发时对于开发者很合理, 但对于它的使用者不够方便,开发者会把程序结构分为很多层,使用者想找到这种深层结构中的某个类型很费劲。
比如:开发的库中有定义my_crate:some_module::another_module:UsefulType这种层次关系,使用者要使用UsefulType就得访问很多层,而实际希望的使用方法是my_crate::UsefulType,能够直接使用。
解决办法: 不需要重新组织内部代码结构,使用pub use: 可以重新导出,创建一个与内部私有结构不同的对外公共结构。
比如例子hello_cargo:
main.rs内容:
use hello_cargo::modu1::EnumColor; use hello_cargo::utils_modu::mix; // 使用者引入很深 fn main() { let r = EnumColor::Blue; mix(r); }
lib.rs内容:
//! # test_crate //! //! A test library //! pub mod modu1 { /// test enum 1 pub enum EnumColor { Red, Blue, } /// test enum 2 pub enum Enum2 { Aaa, Bbb, } } pub mod utils_modu { use crate::modu1::*; /// a test function for pub use pub fn mix(c1: EnumColor) -> Enum2 { Enum2::Aaa } }
使用cargo doc --open生成的文档如下图,也是比较深,不直观。只能看到两个muduler,点进去才能看到enum和函数。
Crate hello_cargoCopy item path source · [−] test_crate A test library Modules 只能看到两个muduler,点进去才能看到enum和函数 modu1 utils_modu
但将lib.rs改成如下导出模式:
//! # test_crate //! //! A test library //! pub use self::modu1::EnumColor; pub use self::modu1::Enum2; pub use self::utils_modu::mix; pub mod modu1 { /// test enum 1 pub enum EnumColor { Red, Blue, } /// test enum 2 pub enum Enum2 { Aaa, Bbb, } } pub mod utils_modu { use crate::modu1::*; /// a test function for pub use pub fn mix(c1: EnumColor) -> Enum2 { Enum2::Aaa } }
再执行cargo doc --open,内容变成如下模式,用户能够直接看到导出的关键信息。
Crate hello_cargoCopy item path source · [−] test_crate A test library Re-exports pub use self::modu1::EnumColor; pub use self::modu1::Enum2; pub use self::utils_modu::mix; 能直接看到函数接口 Modules modu1 utils_modu
main.rs里的引入也可改成如下写法:
// use hello_cargo::modu1::EnumColor; // use hello_cargo::utils_modu::mix; // 使用者引入很深 use hello_cargo::EnumColor; use hello_cargo::mix; //直接引入 fn main() { let r = EnumColor::Blue; mix(r); }
cargo的工作空间(Workspaces)
cargo工作空间,帮助管理多个相互关联且需要协同开发的crate,是一套共享同一个Cargo.lock和输出文件夹的包。
创建工作空间,例子:1二进制crate依赖于2个库crate
1)首先创建workspace目录,使用mkdir workspace_test
,进入workspace_test目录后,创建Cargo.toml文件用于配置整个workspace,内容如下:
[workspace] # 用于配置整个工作空间 # 添加成员,比如二进制成员bin_member members = [ "bin_member" ]
2)创建出配置文件里的bin_member,在workspace_test目录下,执行cargo new bin_member
命令,创建出常规二进制项目,含src/main.rs和Cargo.toml文件。
3)在workspace_test目录下,执行cargo build
命令,可见能正常编译通过了,并产生了Cargo.lock和target目录,target目录用于存放所有成员的编译产出物。二进制成员bin_member则不再有target目录,即便进入bin_member目录执行cargo build也不会产生target目录。
4)继续添加lib成员,在workspace_test目录下,修改workspace_test目录下的Cargo.toml内容如下:
[workspace] # 用于配置整个工作空间 # 添加成员,比如二进制成员 members = [ "bin_member", "adder_member", "logic_member", ]
5)创建出成员lib,
cargo new adder_member --lib cargo new logic_member --lib
其中adder_member/src/lib.rs内容,含有一add函数:
pub fn add(left: usize, right: usize) -> usize { left + right } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let result = add(2, 2); assert_eq!(result, 4); } }
logic_member/src/lib.rs内容,含一bitwise_or函数:
pub fn bitwise_or(left: usize, right: usize) -> usize { left | right } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let result = bitwise_or(5, 2); assert_eq!(result, 7); } }
此时在workspace_test目录下执行cargo test命令就可可执行两个库里的test用例了(跑所有用例),也可执行cargo test -p adder_member
命令只执行一个crate的用例。
6)因为cargo不会隐式假设目录之间依赖,所以需要在bin_member里显式引入,修改bin_member/Cargo.toml引入这两个Lib:
[package] name = "bin_member" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] adder_member = { path = "../adder_member" } logic_member = { path = "../logic_member" }
再修改bin_member/src/main.rs文件内容,引入两个lib:
use adder_member; use logic_member; fn main() { let ret = adder_member::add(2, 3); let ret2 = logic_member::bitwise_or(5, 2); println!("{}, {}", ret, ret2); assert_eq!(5, ret); assert_eq!(7, ret2); }
在workspace_test主目录下执行cargo build和cargo run命令可编译/运行项目。也可通过cargo run -p bin_member
命令指定运行二进制名称运行对应crate。
7)可在工作空间中依赖外部crate,工作空间只有一个Cargo.lock文件,在顶层目录中,可保证工作空间内所有crate使用的依赖版本都相同,使得工作空间所有crate相互兼容。另外,如果要执行cargo pub use,则需要进入每个crate目录下进行发布。
从crate.io安装二进制crate
执行命令:cargo install
, 安装来源:https://crates.io,只能安装具有二进制目标(binary target)的crate。
二进制目标(binary target)是一个可执行程序,当拥有src/main.rs或其它被指定为二进制文件的crate生成。
通常readme里有关于crate是否含有library target或binary target的描述。
cargo install
安装的二进制存放在根目录的bin文件夹中,如果是rustup安装的rust,无任何自定义配置,二进制存放目录是$HOME/.cargo/bin,需确保该目录在环境变量$PATH中。
使用自定义命令扩展cargo
- cargo 被设计成可以使用子命令来扩展
例:如果 $PATH 中的某个二进制是cargo-something,就可以像子命令一样运行: cargo something。 - 类似这样的自定义命令可以通过该命令列出:cargo --list
- 优点: 可使用cargo install 来安装扩展, 像内置工具一样来运行
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战