Rust 学习笔记:Cargo 工作区

news2025/6/5 21:54:51

Rust 学习笔记:Cargo 工作区

  • Rust 学习笔记:Cargo 工作区
    • 创建工作区
    • 在工作区中创建第二个包
    • 依赖于工作区中的外部包
    • 向工作区添加测试
    • 将工作区中的 crate 发布到 crates.io
    • 添加 add_two crate 到工作区
    • 总结

Rust 学习笔记:Cargo 工作区

随着项目的发展,库 crate 可能会越来越大,你可能希望将包(package)进一步拆分为多个库 crate。Cargo 提供了一个称为工作区(Workspaces)的特性,它可以帮助管理串联开发的多个相关包。

创建工作区

工作区是一组共享相同 Cargo.lock 和输出目录的包。

构建工作区有多种方法,我们只展示一种常见的方法。我们将有一个包含二进制文件和两个库的工作区。提供主要功能的二进制文件将依赖于这两个库。一个库提供一个 add_one 函数,另一个库提供一个 add_two 函数。这三个 crate 将是同一个工作区的一部分。

我们首先为工作区创建一个新目录:

$ mkdir add
$ cd add

接下来,在 add 目录中,我们创建 Cargo.toml 文件,它将配置整个工作区。这个文件没有 [package] 部分。相反,它将以一个 [workspace] 部分开始,该部分将允许我们向工作区添加成员。通过将解析器设置为 3,我们还强调在工作区中使用 Cargo 解析器算法的最新和最好版本。

[workspace]
resolver = "3"

接下来,我们将通过在 add 目录中运行 cargo new 来创建加法器二进制 crate:

在这里插入图片描述

在工作区内运行 cargo new 还会自动将新创建的包添加到工作区内 Cargo.toml 的 [workspace] 定义中的 members 键中,如下所示:

在这里插入图片描述

此时,我们可以通过运行 cargo build 来构建工作区。add 目录下的文件应该是这样的:

├── Cargo.lock
├── Cargo.toml
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

工作区在顶层有一个目标目录,编译后的工件将被放置到该目录中。adder 包没有自己的目标目录。即使我们要从 adder 目录中运行 cargo build,编译后的工件仍然会在 add/target 而不是 add/adder/target 中结束。Cargo 在工作区的目标目录中采用这样的结构,因为工作区的 crate 是相互依赖的。如果每个 crate 都有自己的目标目录,那么每个 crate 都必须重新编译工作区中的其他 crate,以便将工件放置在自己的目标目录中。通过共享一个目标目录,crate 可以避免不必要的重新构建。

在工作区中创建第二个包

接下来,让我们在工作区中创建另一个成员包,并将其命名为 add_one。

生成一个名为 add_one 的库 crate:

在这里插入图片描述

顶层的 Cargo.toml 文件现在将在 members 列表中包含 add_one 路径:

在这里插入图片描述

add 目录下的文件树为:

├── Cargo.lock
├── Cargo.toml
├── add_one
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

在 add_one/src/lib.rs 中添加一个 add_one 函数:

pub fn add_one(x: i32) -> i32 {
    x + 1
}

现在,我们让 adder 包依赖 add_one 库。首先,我们需要在 adder/Cargo.toml 上添加一个对 add_one 的路径依赖。

[dependencies]
add_one = { path = "../add_one" }

Cargo 并不假设工作区中的 crate 将相互依赖,因此我们需要明确依赖关系。

接下来,让我们在 adder crate 中使用 add_one 函数,修改 adder/src/main.rs:

fn main() {
    let num = 10;
    println!("Hello, world! {num} plus one is {}!", add_one::add_one(num));
}

在顶级的 add 目录中运行 cargo build 来构建工作区。

要从 add 目录运行二进制 crate,我们可以使用 cargo run 加上 -p 参数后接包名来指定我们想要运行的工作区中的哪个包。

在这里插入图片描述

运行了 adder/src/main.rs 中的代码,代码依赖于 add_one crate。

依赖于工作区中的外部包

注意,工作区在顶层只有一个 Cargo.lock 文件,而不是在每个 crate 的目录中都有一个 Cargo.lock。这确保了所有的 crate 都使用相同版本的所有依赖项。

如果我们将 rand 包添加到 adder/Cargo.toml 和 add_one/Cargo.toml 文件中,Cargo 将把这两个包解析为一个 rand 版本,并将其记录在一个 Cargo.lock 中。

让工作区中的所有 crate 使用相同的依赖关系意味着这些 crate 将始终相互兼容。让我们将 rand crate 添加到 add_one/Cargo.toml 文件的 [dependencies] 部分,这样我们就可以在 add_one crate 中使用 rand crate 了:

[dependencies]
rand = "0.8.5"

我们现在可以在 add_one/src/lib.rs 中添加 use rand;。通过在 add 目录中运行 cargo build 来构建整个工作区,将引入并编译 rand crate。我们将得到一个警告,因为我们没有引用我们带入范围的 rand:

在这里插入图片描述

即使在工作区的某个地方使用了 rand,我们也不能在工作区的其他 crate 中使用它,除非我们也将 rand 添加到它们的 Cargo.toml 文件中。

例如,如果我们将 use rand; 添加到 adder/src/main.rs 文件中,我们将得到一个错误:

在这里插入图片描述

要解决这个问题,需要编辑 adder/Cargo.toml 文件,并指出 rand 也是它的依赖项。

[dependencies]
add_one = { path = "../add_one"}
rand = "0.8.5"

构建 adder 包会将 rand 添加到 Cargo.lock 中 adder 的依赖列表中,但不会下载 rand 的其他副本。Cargo 的语义版本规则将确保工作区中使用 rand 包的每个包中的每个 crate 使用相同的版本,只要它们指定 rand 的兼容版本,就可以节省空间,并确保工作区中的 crate 彼此兼容。

假设同一工作区中有一个 crate 依赖 rand 0.8.0,另一个 crate 依赖 rand 0.8.1。根据语义版本规则,这两个语义版本是兼容的,所以这两个 crate 都使用 rand 0.8.1,或者使用更新的补丁版本,比如 0.8.2。

如果工作区中的 crate 指定了相同依赖项的不兼容版本,Cargo 将解析每个版本,但仍将尝试解析尽可能少的版本。

假设同一工作区中有一个 crate 依赖 rand 0.8.0,另一个 crate 依赖 rand 0.7.0。因为语义版本不兼容,Cargo 为每个 crate 使用不同版本的 rand。

向工作区添加测试

在 add_one crate 中添加一个对 add_one 函数的测试:

pub fn add_one(x: i32) -> i32 {
    x + 1
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(3, add_one(2));
    }
}

现在在顶级 add 目录中运行 cargo test。在这样的工作区中运行 cargo test 将为工区中的所有 crate 运行测试:

在这里插入图片描述

输出的第一部分显示通过了 add_one crate 中的 it_works 测试。下一节显示在 adder crate 中没有找到任何测试,最后一节显示在 add_one crate 中没有找到任何文档测试。

我们还可以通过使用 -p 标志并指定我们想要测试的 crate 的名称,在顶层目录下对工作区中的特定 crate 运行测试:

在这里插入图片描述

该输出显示 cargo test 只运行了add_one crate 的测试,而没有运行 adder crate 的测试。

将工作区中的 crate 发布到 crates.io

如果将工作区中的 crate 发布到 crates.io,那么工作区中的每个 crate 都需要单独发布。与 cargo test 类似,我们可以通过使用 -p 标志并指定我们想要发布的 crate 的名称,在工作区中发布特定的 crate。

添加 add_two crate 到工作区

以与 add_one crate 类似的方式将 add_two crate 添加到该工作区。

在这里插入图片描述

adder/Cargo.toml:

[dependencies]
add_one = { path = "../add_one"}
add_two = { path = "../add_two"}
rand = "0.8.5"

在 add_two/src/lib.rs 中添加以下代码:

pub fn add_two(x: i32) -> i32 {
    x + 2
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(4, add_two(2));
    }
}

修改 adder/src/main.rs:

fn main() {
    let num = 10;
    println!("Hello, world! {num} plus one is {}!", add_one::add_one(num));
    println!("Hello, world! {num} plus two is {}!", add_two::add_two(num));
}

运行 cargo run:

在这里插入图片描述

运行 cargo test:

PS C:\Users\81228\Documents\Program\Rust Project\add> cargo test
   Compiling add_two v0.1.0 (C:\Users\81228\Documents\Program\Rust Project\add\add_two)
   Compiling adder v0.1.0 (C:\Users\81228\Documents\Program\Rust Project\add\adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.43s                                                                                                           
     Running unittests src\lib.rs (target\debug\deps\add_one-a59b5500767c3a29.exe)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src\lib.rs (target\debug\deps\add_two-927f837920a25f8c.exe)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src\main.rs (target\debug\deps\adder-30ef0042878a10a4.exe)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests add_one

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests add_two

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

运行 cargo test -p add_two:

在这里插入图片描述

总结

随着项目的增长,请考虑使用工作区:它使得开发者能够使用更小、更容易理解的组件,而不是使用一大块代码。此外,如果经常同时更改,则将 crate 保存在工作区中可以使它们之间的协调更容易。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2398060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

颈部的 “异常坚持”

生活中,有些人的颈部会突然变得 “异常坚持”—— 头部不受控制地偏向一侧,或是不自主地旋转、后仰,仿佛被无形的力量牵引着。这种情况不仅影响外观,还会带来强烈的不适感,颈部肌肉紧绷、酸痛,像被一根绳索…

Ubuntu22.04安装MinkowskiEngine

MinkowskiEngine简介 Minkowski引擎是一个用于稀疏张量的自动微分库。它支持所有标准神经网络层,例如对稀疏张量的卷积、池化和广播操作。 MinkowskiEngine安装 官方源码链接:GitHub - NVIDIA/MinkowskiEngine: Minkowski Engine is an auto-diff neu…

【计算机网络】第2章:应用层—应用层协议原理

目录 1. 网络应用的体系结构 2. 客户-服务器(C/S)体系结构 3. 对等体(P2P)体系结构 4. C/S 和 P2P 体系结构的混合体 Napster 即时通信 5. 进程通信 6. 分布式进程通信需要解决的问题 7. 问题1:对进程进行编址…

【Zephyr 系列 6】使用 Zephyr + BLE 打造蓝牙广播与连接系统(STEVAL-IDB011V1 实战)

🧠关键词:Zephyr、BLE、广播、连接、GATT、低功耗蓝牙、STEVAL-IDB011V1 📌适合人群:希望基于 Zephyr 实现 BLE 通信的嵌入式工程师、蓝牙产品开发人员 🧭 前言:为什么选择 Zephyr 开发 BLE? 在传统 BLE 开发中,我们大多依赖于厂商 SDK(如 Nordic SDK、BlueNRG SD…

利用 Scrapy 构建高效网页爬虫:框架解析与实战流程

目录 前言1 Scrapy 框架概述1.1 Scrapy 的核心优势1.2 Scrapy 的典型应用场景 2 Scrapy 工作原理解析2.1 框架结构图2.2 Spider:定义数据采集策略2.3 Scheduler:调度请求与去重2.4 Downloader:网页下载器2.5 Item:结构化数据容器2…

RPG20.创建敌人的初始能力和加载武器

1. 基于StartUpAbilitiy基类创建专门用于敌人数据的DAStartUpABility,然后再基于新创建的DA再创建一个蓝图 2.打开 DataAsset_EnemyStartUpData.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "Cor…

自适应移动平均(Adaptive Moving Average, AMA)

文章目录 1. 考夫曼自适应移动平均 (KAMA)算法推导及Python实现2. 对 (KAMA)算法参数进行优化及实现 自适应移动平均(Adaptive Moving Average, AMA)由Perry Kaufman在其著作《Trading Systems and Methods》中提出,它通过动态调整平滑系数来…

涨薪技术|0到1学会性能测试第95课-全链路脚本开发实例

至此关于系统资源监控、apache监控调优、Tomcat监控调优、JVM调优、Mysql调优、前端监控调优、接口性能监控调优的知识已分享完,今天学习全链路脚本开发知识。后续文章都会系统分享干货,带大家从0到1学会性能测试。 前面章节介绍了如何封装.h头文件,现在通过一个实例来介绍…

Spring AI Alibaba + Nacos 动态 MCP Server 代理方案

作者:刘宏宇,Spring AI Alibaba Contributor 文章概览 Spring AI Alibaba MCP 可基于 Nacos 提供的 MCP server registry 信息,建立一个中间代理层 Java 应用,将 Nacos 中注册的服务信息转换成 MCP 协议的服务器信息&#xff0c…

MCP:让AI工具协作变得像聊天一样简单 [特殊字符]

想象一下,你正在处理一个项目,需要从A平台查看团队讨论,从B平台获取客户信息,还要在GitHub上检查代码进度。传统做法是什么?打开三个不同的网页,在各个平台间来回切换,复制粘贴数据,最后还可能因为信息分散而遗漏重要细节。 听起来很熟悉?这正是当前工作流程的痛点所…

AI炼丹日志-27 - Anubis 通过 PoW工作量证明的反爬虫组件 上手指南 原理解析

点一下关注吧!!!非常感谢!!持续更新!!! Java篇: MyBatis 更新完毕目前开始更新 Spring,一起深入浅出! 大数据篇 300: Hadoop&…

阿姆达尔定律的演进:古斯塔夫森定律

前言 在上一篇文章《使用阿姆达尔定律来提升效率》中提到的阿姆达尔定律前提是假设问题的规模保持不变,并且给定一台速度更快的机器,目标是更快地解决问题。然而,在大多数情况下,这并不完全正确。当有一台更快的机器时&#xff0…

JavaScript极致性能优化全攻略

JavaScript性能优化深度指南 1 引言 JavaScript性能优化在现代Web开发中至关重要。随着Web应用日益复杂,性能直接影响用户体验、搜索引擎排名和业务转化率。研究表明,页面加载时间每增加1秒,转化率下降7%,跳出率增加32%。通过优化JavaScript性能,开发者可以: 提升用户满…

Transformer核心原理

简介 在人工智能技术飞速发展的今天,Transformer模型凭借其强大的序列处理能力和自注意力机制,成为自然语言处理、计算机视觉、语音识别等领域的核心技术。本文将从基础理论出发,结合企业级开发实践,深入解析Transformer模型的原…

Grafana-State timeline状态时间线

显示随时间推移的状态变化 状态区域:即状态时间线上的状态显示的条或带,区域长度表示状态持续时间或频率 数据格式要求(可视化效果最佳): 时间戳实体名称(即:正在监控的目标对应名称&#xf…

解决CSDN等网站访问不了的问题

原文网址:解决CSDN等网站访问不了的问题-CSDN博客 简介 本文介绍解决CSDN等网站访问不了的方法。 问题描述 CSDN访问不了了,页面是空的。 问题解决 方案1:修改DNS 可能是dns的问题,需要重新配置。 国内常用的dns是&#x…

C++ Vector算法精讲与底层探秘:从经典例题到性能优化全解析

前引:在C标准模板库(STL)中,vector作为动态数组的实现,既是算法题解的基石,也是性能优化的关键战场。其连续内存布局、动态扩容机制和丰富的成员函数,使其在面试高频题(如LeetCode、…

Flowith,有一种Agent叫无限

大家好,我是羊仔,专注AI工具、智能体、编程。 今天羊仔要和大家聊聊一个最近发现的超级实用的Agent平台,名字叫Flowith。 这篇文章会带你从零了解到实战体验,搞清楚Flowith是如何让工作效率飙升好几倍,甚至重新定义未…

系统思考:短期利益与长期系统影响

一个决策难题:一家公司接到了一个大订单,客户提出了10%的降价要求,而企业的产能还无法满足客户的需求。你会选择增加产能,接受这个订单,还是拒绝?从系统思考的角度来看,这个决策不仅仅是一个简单…

HTTP连接管理——短连接,长连接,HTTP 流水线

连接管理是一个 HTTP 的关键话题:打开和保持连接在很大程度上影响着网站和 Web 应用程序的性能。在 HTTP/1.x 里有多种模型:短连接、_长连接_和 HTTP 流水线。 下面分别来详细解释 短连接 HTTP 协议最初(0.9/1.0)是个非常简单的…