带有库和二进制文件的 Rust 包?

Posted

技术标签:

【中文标题】带有库和二进制文件的 Rust 包?【英文标题】:Package with both a library and a binary? 【发布时间】:2015-01-12 19:51:42 【问题描述】:

我想制作一个 Rust 包,其中包含一个可重用的库(大部分程序都在其中实现)和一个使用它的可执行文件。

假设我没有混淆 Rust 模块系统中的任何语义,我的 Cargo.toml 文件应该是什么样的?

【问题讨论】:

【参考方案1】:
Tok:tmp doug$ du -a

8   ./Cargo.toml
8   ./src/bin.rs
8   ./src/lib.rs
16  ./src

Cargo.toml:

[package]
name = "mything"
version = "0.0.1"
authors = ["me <me@gmail.com>"]

[lib]
name = "mylib"
path = "src/lib.rs"

[[bin]]
name = "mybin"
path = "src/bin.rs"

src/lib.rs:

pub fn test() 
    println!("Test");

src/bin.rs:

extern crate mylib; // not needed since Rust edition 2018

use mylib::test;

pub fn main() 
    test();

【讨论】:

谢谢道格,我会试试的!那么 #![crate_name= ] 和 #![crate_type] 注释是可选的吗? 使用 Cargo 时,这些选项是不必要的,因为 Cargo 将它们作为编译器标志传递。如果您运行cargo build --verbose,您将在rustc 命令行中看到它们。 你知道为什么[[bin]] 是一个表数组吗?为什么使用[[bin]] 而不是[bin]?似乎没有这方面的任何文档。 @CMCDragonkai 是 toml 格式规范 [[x]] 是一个反序列化后的数组; IE。单个 crate 可能会生成多个二进制文件,但只能生成一个库(因此是 [lib],而不是 [[lib]])。您可以有多个 bin 部分。 (我同意,这看起来很奇怪,但 toml 一直是一个有争议的选择)。 当我想要的只是库时,有没有办法阻止它编译二进制文件?该二进制文件具有我通过称为“二进制”的功能添加的附加依赖项,当我尝试在没有该功能的情况下编译它时,它无法构建。它抱怨找不到 bin.rs 尝试导入的 crates。【参考方案2】:

简单

创建一个src/main.rs,它将用作事实上的可执行文件。您无需修改​​您的Cargo.toml,此文件将被编译为与库同名的二进制文件。

项目内容:

% tree
.
├── Cargo.toml
└── src
    ├── lib.rs
    └── main.rs

Cargo.toml

[package]
name = "example"
version = "0.1.0"
edition = "2018"

src/lib.rs

use std::error::Error;

pub fn really_complicated_code(a: u8, b: u8) -> Result<u8, Box<dyn Error>> 
    Ok(a + b)

src/main.rs

fn main() 
    println!(
        "I'm using the library: :?",
        example::really_complicated_code(1, 2)
    );

并执行它:

% cargo run -q
I'm using the library: Ok(3)

灵活

如果您希望控制二进制文件的名称或拥有多个二进制文件,您可以在src/bin 中创建多个二进制源文件,在src 中创建其余的库源文件。您可以在my project 中查看示例。你根本不需要修改你的Cargo.tomlsrc/bin中的每个源文件都会被编译成同名的二进制文件。

项目内容:

% tree
.
├── Cargo.toml
└── src
    ├── bin
    │   └── mybin.rs
    └── lib.rs

Cargo.toml

[package]
name = "example"
version = "0.1.0"
edition = "2018"

src/lib.rs

use std::error::Error;

pub fn really_complicated_code(a: u8, b: u8) -> Result<u8, Box<dyn Error>> 
    Ok(a + b)

src/bin/mybin.rs

fn main() 
    println!(
        "I'm using the library: :?",
        example::really_complicated_code(1, 2)
    );

并执行它:

% cargo run --bin mybin -q
I'm using the library: Ok(3)

另见:

How can I specify which crate `cargo run` runs by default in the root of a Cargo workspace?

【讨论】:

非常适合 rust 的约定优于配置的方法!两个答案一起,您将获得极大的便利性和灵活性。 extern crate example; 从 rust 2018 开始不需要,你可以直接写use example::really_complicated_code; 并使用函数而不用命名范围【参考方案3】:

您可以将lib.rsmain.rs 放在源文件夹中。 没有冲突,cargo 会构建这两个东西。

要解决文档冲突,请添加到您的Cargo.toml

[[bin]]
name = "main"
doc = false

【讨论】:

这将被“另外,您可以创建一个 src/main.rs 用作事实上的可执行文件”。在另一个答案中,不是吗?并且文档冲突由接受的答案解决,对吗?您可能需要澄清您的答案,以说明为什么这是独一无二的。可以参考其他答案来构建它们。【参考方案4】:

另一种解决方案是不要试图将两种东西都塞进一个包中。对于具有友好可执行文件的稍微大一点的项目,我发现使用workspace 非常好。

在这里,我创建了一个二进制项目,其中包含一个库,但组织代码的方法有很多:

 % tree the-binary
the-binary
├── Cargo.toml
├── src
│   └── main.rs
└── the-library
    ├── Cargo.toml
    └── src
        └── lib.rs

Cargo.toml

这使用[workspace] 键并且依赖于库:

[package]
name = "the-binary"
version = "0.1.0"
edition = "2018"

[workspace]

[dependencies]
the-library =  path = "the-library" 

src/main.rs

fn main() 
    println!(
        "I'm using the library: :?",
        the_library::really_complicated_code(1, 2)
    );

the-library/Cargo.toml

[package]
name = "the-library"
version = "0.1.0"
edition = "2018"

the-library/src/lib.rs

use std::error::Error;

pub fn really_complicated_code(a: u8, b: u8) -> Result<u8, Box<dyn Error>> 
    Ok(a + b)

并执行它:

% cargo run -q
I'm using the library: Ok(3)

这个方案有两大好处:

    二进制文件现在可以使用仅适用于它的依赖项。例如,您可以包含许多 crate 来改善用户体验,例如命令行解析器或终端格式化。这些都不会“感染”库。

    工作区可防止每个组件的冗余构建。如果我们在the-librarythe-binary 目录中都运行cargo build,则不会两次都构建库——它在两个项目之间共享。

【讨论】:

这似乎是一个更好的方法。显然,这个问题被问到已经有好几年了,但人们仍然在为组织大型项目而苦苦挣扎。与上面选择的答案相比,使用工作区是否有缺点? @Jspies 我能想到的最大缺点是有些工具并不完全知道如何处理工作空间。当与具有某种“项目”概念的现有工具交互时,它们有点奇怪。我个人倾向于采用连续的方法:我从 main.rs 中的所有内容开始,然后随着它变大将其分解为模块,最后在稍大一点时拆分为 src/bin,然后在我开始时转移到工作区大量重用核心逻辑。 谢谢,我会试一试。我当前的项目有几个库,它们是作为项目的一部分开发的,但也可以在外部使用。 它构建并运行良好,但cargo test 似乎忽略了 lib.rs 中的单元测试 @Stein 我想你想要cargo test --all

以上是关于带有库和二进制文件的 Rust 包?的主要内容,如果未能解决你的问题,请参考以下文章

rust 模块组织结构

有没有办法在 Rust 库中包含二进制或文本文件?

使用 rust 二进制文件运行 docker 映像时出现权限被拒绝错误

由于 rustc 没有“-fPIC”标志,如何将 rust 代码编译为 PIC 二进制文件?

动态库和静态库

如何“使用”或导入本地 Rust 文件? [复制]