Gói Rust với cả thư viện và nhị phân?


190

Tôi muốn tạo một gói Rust chứa cả thư viện có thể sử dụng lại (nơi hầu hết chương trình được triển khai) và cũng có thể thực thi được sử dụng nó.

Giả sử tôi không nhầm lẫn bất kỳ ngữ nghĩa nào trong hệ thống mô-đun Rust, Cargo.tomltập tin của tôi sẽ trông như thế nào?

Câu trả lời:


205
Tok:tmp doug$ du -a

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

Vận chuyển hàng hóa:

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

2
Cảm ơn Doug, tôi sẽ thử nó! Có phải các chú thích #! [Crate_name =] và #! [Crate_type] không?
Andrew Wagner

4
Khi bạn sử dụng Cargo, các tùy chọn này là không cần thiết vì Cargo chuyển chúng dưới dạng cờ biên dịch. Nếu bạn chạy cargo build --verbose, bạn sẽ thấy chúng trong rustcdòng lệnh.
Vladimir Matveev

33
Bạn có biết tại sao [[bin]]một mảng các bảng không? Tại sao sử dụng [[bin]]và không [bin]? Dường như không có bất kỳ tài liệu về điều này.
CMCDragonkai

40
@CMCDragonkai Đó là đặc tả định dạng toml [[x]] là một mảng đã được khử lưu; I E. một thùng duy nhất có thể tạo ra nhiều nhị phân, nhưng chỉ một thư viện (do đó [lib], không phải [[lib]]). Bạn có thể có nhiều phần bin. (Tôi đồng ý, điều này có vẻ kỳ lạ, nhưng toml luôn là một lựa chọn gây tranh cãi).
Doug

1
Có cách nào để ngăn nó biên dịch nhị phân khi tất cả những gì tôi muốn là lib không? Nhị phân có các phụ thuộc bổ sung mà tôi thêm thông qua một tính năng gọi là "nhị phân", khi tôi cố gắng biên dịch nó mà không có tính năng đó, nó không thể xây dựng được. Nó phàn nàn rằng nó không thể tìm thấy các thùng mà bin.rs đang cố gắng nhập.
Person93

150

Bạn cũng có thể chỉ cần đặt các nguồn nhị phân vào src/binvà phần còn lại của các nguồn của bạn src. Bạn có thể thấy một ví dụ trong dự án của tôi . Bạn hoàn toàn không cần phải sửa đổi Cargo.tomlvà mỗi tệp nguồn sẽ được biên dịch thành tệp nhị phân cùng tên.

Cấu hình của câu trả lời khác sau đó được thay thế bằng:

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

Vận chuyển hàng hóa

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

Và thực hiện nó:

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

Ngoài ra, bạn chỉ có thể tạo một src/main.rscái sẽ được sử dụng như là tệp thực thi defacto. Thật không may, điều này mâu thuẫn với cargo doclệnh:

Không thể ghi lại một gói trong đó thư viện và tệp nhị phân có cùng tên. Xem xét đổi tên một hoặc đánh dấu mục tiêu làdoc = false


13
rất phù hợp với phương pháp tiếp cận cấu hình quá mức của rỉ sét! cả hai câu trả lời cùng nhau và bạn có một số thuận tiện và linh hoạt tuyệt vời.
cừu bay

9
extern crate example;không bắt buộc kể từ rỉ sét 2018, bạn có thể trực tiếp viết use example::really_complicated_code;và sử dụng chức năng mà không cần đặt tên phạm vi
sassman

47

Một giải pháp thay thế là không thực sự cố gắng nhồi nhét cả hai thứ vào một gói. Đối với các dự án lớn hơn một chút với khả năng thực thi thân thiện, tôi đã thấy rất tuyệt khi sử dụng không gian làm việc

Chúng tôi tạo một dự án nhị phân bao gồm một thư viện bên trong nó:

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

Vận chuyển hàng hóa

Điều này sử dụng [workspace]khóa và phụ thuộc vào thư viện:

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

thư viện của tôi / src / lib.rs

use std::error::Error;

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

Và thực hiện nó:

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

Có hai lợi ích lớn cho chương trình này:

  1. Hiện tại nhị phân có thể sử dụng các phụ thuộc chỉ áp dụng cho nó. Ví dụ: bạn có thể bao gồm nhiều thùng để cải thiện trải nghiệm người dùng, chẳng hạn như trình phân tích cú pháp dòng lệnh hoặc định dạng thiết bị đầu cuối. Không ai trong số này sẽ "lây nhiễm" thư viện.

  2. Không gian làm việc ngăn chặn các bản dựng dự phòng của từng thành phần. Nếu chúng tôi chạy cargo buildtrong cả thư mục mylibrarythe-binarythư mục, thư viện sẽ không được xây dựng cả hai lần - nó được chia sẻ giữa cả hai dự án.


Đây có vẻ là một cách tốt hơn để đi. Rõ ràng đã nhiều năm kể từ khi câu hỏi được đặt ra nhưng mọi người vẫn đấu tranh với việc tổ chức các dự án lớn. Có một nhược điểm nào khi sử dụng không gian làm việc so với câu trả lời được chọn ở trên không?
Jspies

4
@Jspies nhược điểm lớn nhất tôi có thể nghĩ ra khỏi đỉnh đầu của tôi là có một số công cụ không biết cách xử lý các không gian làm việc. Chúng là một điểm kỳ lạ khi tương tác với các công cụ hiện có có khái niệm "dự án" nào đó. Cá nhân tôi có xu hướng thực hiện một cách tiếp cận liên tục: Tôi bắt đầu với mọi thứ trong main.rsđó, sau đó chia nó thành các mô-đun khi nó lớn hơn, cuối cùng tách ra src/binkhi nó chỉ lớn hơn một chút, sau đó chuyển sang không gian làm việc khi tôi bắt đầu sử dụng lại logic cốt lõi.
Người quản lý

cảm ơn tôi sẽ cho nó một spin dự án hiện tại của tôi có một vài lib được phát triển như một phần của dự án nhưng cũng được sử dụng bên ngoài.
Jspies

Nó xây dựng và chạy tốt, nhưng cargo testdường như bỏ qua các bài kiểm tra đơn vị trong lib.rs
Stein

3
@Stein tôi nghĩ bạn muốn cargo test --all
Shepmaster

18

Bạn có thể đặt lib.rsmain.rsvào thư mục nguồn cùng nhau. Không có xung đột và hàng hóa sẽ xây dựng cả hai thứ.

Để giải quyết xung đột tài liệu, hãy thêm vào Cargo.toml:

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

3
Điều đó sẽ được bao phủ bởi " Ngoài ra, bạn chỉ có thể tạo một src / main.rs sẽ được sử dụng làm tệp thực thi defacto ". trong câu trả lời khác, không? Và xung đột tài liệu được giải quyết bằng câu trả lời được chấp nhận, phải không? Bạn có thể cần phải làm rõ câu trả lời của bạn để cho thấy tại sao điều này là duy nhất. Bạn có thể tham khảo các câu trả lời khác để xây dựng chúng.
Người quản lý
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.