【翻译】Rust包如何既有一个库又有一个可执行文件?

问题:

Rust package with both a library and a binary?

Rust包如何既有一个库又有一个可执行文件?


I would like to make a Rust package that contains both a reusable library (where most of the program is implemented), and also an executable that uses it.

我想创建一个Rust包,它既包含一个可重复使用的库,程序的大部分地方是它来实现的,同时也包含一个可执行的文件来使用这个库。


Assuming I have not confused any semantics in the Rust module system, what should my Cargo.toml file look like?

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


答案1(selected)

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();

}

Thanks Doug, I will try it! Are the #![crate_name= ] and #![crate_type] annotations optional then? 

感谢Doug,我会试一下!#![crate_name= ] 和 #![crate_type] 这两个注释是可选的么?


When you use Cargo, these options are unnecessary because Cargo passes them as compiler flags. If you run cargo build --verbose, you'll see them in rustc command line.

当你使用Cargo时,这些选项都是不必要的,因为Cargo作为编译器标记传递他们。如果你运行 cargo build --verbose,你会在rustc命令行看到他们。


Do you know why [[bin]] is an array of tables? Why use [[bin]] and not [bin]? There doesn't seem to be any documentation on this.

你知道为什么[[bin]]是一个数组么?为什么使用 [[bin]]而不是[bin]?似乎没有任何关于这个的文档。


@CMCDragonkai It's the toml format specification [[x]] is an array once deserialized; ie. a single crate may produce multiple binaries, but only one library (thus [lib], not [[lib]]). You can have multiple bin sections. (I agree, this looks weird, but toml was always a controversial choice)

@CMCDragonkai 这是toml格式规范。 [[x]]是反序列化后的数组。比如,一个单独的crate可能产生多个可执行文件,但是仅仅有一个库(那么是[lib]不是[[lib]])。你能够有多个bin的部分。(我同意,这个看起来有些奇怪,但是toml总是一个有争议的选择)


Is there a way to prevent it from compiling the binary when all I want is the lib? The binary has additional dependencies which I add through a feature called "binary", when I try to compile it without that feature, it fails to build. It complains that it can't find the crates that bin.rs is trying to import.

当我只想是lib,有没有办法阻止他编译为可执行文件?这个可执行文件有额外的依赖,我通过叫做"binary"特性添加的。当我尝试没有那个特性进行编译,构建就会失败。它抱怨找不到bin.rs试图导入的那个crate。


答案2

You can also just put binary sources in src/bin and the rest of your sources in src. You can see an example in my project. You do not need to modify your Cargo.toml at all, and each source file will be compiled to a binary of the same name.

你也可以将可执行源代码放在src/bin目录中,其余代码放在src目录中。你可以看这个例子 my project。你根本不需要修改你的Cargo.toml,并且每个源文件都会被编译到一个同名的二进制文件中。


The other answer’s configuration is then replaced by:

其他回答的配置文件替换为:

$ tree

.

├── Cargo.toml

└── src

   ├── bin

   │   └── mybin.rs

   └── lib.rs

Cargo.toml

[package]

name = "example"

version = "0.0.1"

authors = ["An Devloper <an.devloper@example.com>"]

src/lib.rs

use std::error::Error;

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

    Ok(a + b)

}

src/bin/mybin.rs

extern crate example; // Optional in Rust 2018

fn main() {

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

}

And execute it:

$ cargo run --bin mybin

I'm using the library: Ok(3)

Additionally, you can just create a src/main.rs that will be used as the defacto executable. Unfortunately, this conflicts with the cargo doc command:

另外,你可以只创建一个src/main.rs,将作为实际上可执行文件。不幸的是,这个会和cargo doc 命令冲突。


Cannot document a package where a library and a binary have the same name. Consider renaming one or marking the target as doc = false

当库和可执行文件有相同名字时,不能记录为一个包。考虑重命名或者标记目标为doc = false


fits well with rust’s convention-over-configuration approach! both answers together and you have some great convenience and flexibility.

非常符合rust的约定优于配置的方法!结合两个答案,你会有一些很大方便性和灵活性。


extern crate example; is not required as of rust 2018, you can directly write use example::really_complicated_code; and use the function without naming the scope

extern crate example;不要求rust 2018版本,你能够直接写 use example::really_complicated_code;,使用函数方法时,不需要增加名字范围extern crate example;


答案3

An alternate solution is to not actually try to cram both things into one package. For slightly larger projects with a friendly executable, I've found it very nice to use a workspace

另一个解决方法是不需要尝试将两个东西塞到一个包里。对于稍微大一些的项目,为了使他友好的执行,我发现使用workspace非常好。


We create a binary project that includes a library inside of it:

我们创建一个二进制项目,在其中它包含了一个库:

the-binary

├── Cargo.lock

├── Cargo.toml

├── mylibrary

│   ├── Cargo.toml

│   └── src

│      └── lib.rs

└── src

    └── main.rs

Cargo.toml

This uses the [workspace] key and depends on the library:

这里使用[workspace]关键字,以及依赖的库:

[package]

name = "the-binary"

version = "0.1.0"

authors = ["An Devloper <an.devloper@example.com>"]

[workspace]

[dependencies]

mylibrary = { path = "mylibrary" }

src/main.rs

extern crate mylibrary;

fn main() {

    println!("I'm using the library: {:?}", mylibrary::really_complicated_code(1, 2));

}

mylibrary/src/lib.rs

use std::error::Error;

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

    Ok(a + b)

}

And execute it:

执行它:

$ cargo run

  Compiling mylibrary v0.1.0 (file:///private/tmp/the-binary/mylibrary)

  Compiling the-binary v0.1.0 (file:///private/tmp/the-binary)

    Finished dev [unoptimized + debuginfo] target(s) in 0.73 secs

    Running `target/debug/the-binary`

I'm using the library: Ok(3)

There are two big benefits to this scheme:

这个模式有两个大优点:

1. The binary can now use dependencies that only apply to it. For example, you can include lots of crates to improve the user experience, such as command line parsers or terminal formatting. None of these will "infect" the library.

1. 现在二进制能够使用这些仅适用于它的依赖项。举个例子,你能够包含许多crate,提升用户的体验,比如命令行解析器或者终端格式化。这些没有一个会”感染“库。


2. The workspace prevents redundant builds of each component. If we run cargo build in both the mylibrary and the-binary directory, the library will not be built both times — it's shared between both projects.

2. 工作空间防止多余的构建每一个部分。如果我们在mylibrary和the-binary目录下运行cargo build,库不会被构建两次—它被两个项目共享。


This seems like a much better way to go. Obviously it's been years since the question was asked but people still struggle with organizing large projects. Is there a downside to using a workspace versus the selected answer above?

这个似乎是一个更好的方法。显然,这个问题已经被提出很多年,但是人们仍然在与组织大型项目斗争。与上面选择的答案,使用工作空间有什么缺点么?


@Jspies the biggest downside I can think of off the top of my head is that there are some tools that don't fully know how to deal with workspaces. They are kind of in a weird spot when interacting with existing tools that have some sort of "project" concept. I personally tend to take a continuum approach: I start with everything in main.rs, then break it up into modules as it gets bigger, finally splitting to src/bin when it's just a little bigger, then moving to a workspace when I start heavily reusing the core logic.

@Jspies 我能直接想到的最大的缺点是一些工具不能完全知道怎么样处理工作空间。当与存在一些项目概念的工具进行交互时,他们有一些奇怪的斑点。我个人倾向采用一个连续的方法:我在main.rs中开始所有的事情。然后在它变得更大时,把他分解成模块,最后当它稍微大一点儿的时候,分解成src/bin,当我开始重新使用核心逻辑,则移动到一个空间中。


thanks I will give it a spin. my current project has a couple of libs that are developed as part of the project but also used externally.

谢谢,我将给它一个spin。我当前的项目有两个库,它们作为项目的一部分被开发,但是使用于外部。


It builds and runs fine, but cargo test seems to ignore unit tests in lib.rs

它创建和运行时可以的,但是cargo test 似乎忽略了lib.rs的单元测试。


@Stein I think you want cargo test --all

@Stein 我想你是想cargo test --all

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341