Rust package with both a library and a binary?

2019-01-07 03:58发布

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.

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

4条回答
冷血范
2楼-- · 2019-01-07 04:39

You can put lib.rs and main.rs to sources folder together. There is no conflict and cargo will build both things.

To resolve documentaion conflict add to your Cargo.toml:

[[bin]]
name = "main"
doc = false
查看更多
叼着烟拽天下
3楼-- · 2019-01-07 05:00
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;

use mylib::test;

pub fn main() {
    test();
}
查看更多
Evening l夕情丶
4楼-- · 2019-01-07 05:05

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.

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;

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:

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

查看更多
一夜七次
5楼-- · 2019-01-07 05:05

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

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:

[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.

  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.

查看更多
登录 后发表回答