web-dev-qa-db-fra.com

Rust paquet avec à la fois une bibliothèque et un binaire?

Je voudrais créer un paquet Rust qui contient à la fois une bibliothèque réutilisable (où la plupart du programme est implémenté) et un exécutable qui l’utilise.

En supposant que je n’ai confondu aucune sémantique dans le système de modules Rust, que devrait mon Cargo.toml fichier ressemble?

119
Andrew Wagner
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 <[email protected]>"]

[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();
}
136
Doug

Vous pouvez aussi simplement mettre des sources binaires dans src/bin et le reste de vos sources dans src. Vous pouvez voir un exemple dans mon projet . Vous n'avez pas besoin de modifier votre Cargo.toml du tout, et chaque fichier source sera compilé en un binaire du même nom.

La configuration de l’autre réponse est alors remplacée par:

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

Cargo.toml

[package]
name = "example"
version = "0.0.1"
authors = ["An Devloper <[email protected]>"]

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

Et l'exécuter:

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

De plus, vous pouvez simplement créer un src/main.rs qui sera utilisé comme exécutable de facto. Malheureusement, cela entre en conflit avec le cargo doc commande:

Impossible de documenter un package dans lequel une bibliothèque et un binaire ont le même nom. Pensez à en renommer un ou à marquer la cible comme doc = false

104
Shepmaster

Une autre solution consiste à ne pas essayer de regrouper les deux choses en un seul paquet. Pour des projets légèrement plus grands avec un exécutable convivial, j'ai trouvé très agréable d'utiliser un espace de travail

Nous créons un projet binaire qui inclut une bibliothèque à l'intérieur:

the-binary
├── Cargo.lock
├── Cargo.toml
├── mylibrary
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
└── src
    └── main.rs

Cargo.toml

Ceci utilise le [workspace] clé et dépend de la bibliothèque:

[package]
name = "the-binary"
version = "0.1.0"
authors = ["An Devloper <[email protected]>"]

[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)
}

Et l'exécuter:

$ 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)

Ce schéma présente deux grands avantages:

  1. Le binaire peut maintenant utiliser des dépendances qui ne s’y appliquent que. Par exemple, vous pouvez inclure de nombreuses caisses pour améliorer l'expérience utilisateur, telles que des analyseurs syntaxiques en ligne de commande ou le formatage du terminal. Aucun de ceux-ci "n'infectera" la bibliothèque.

  2. L'espace de travail empêche les générations redondantes de chaque composant. Si nous courons cargo build dans mylibrary et the-binary _ répertoire, la bibliothèque ne sera pas construite les deux fois - elle est partagée entre les deux projets.

22
Shepmaster

Tu peux mettre lib.rs et main.rs dans le dossier sources ensemble. Il n'y a pas de conflit et la cargaison construira les deux choses.

Pour résoudre le conflit documentaire, ajoutez à votre Cargo.toml:

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