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.
Assuming I have not confused any semantics in the Rust module system, what should my Cargo.toml file look like?

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

Simple
Create a src/main.rs that will be used as the defacto executable. You do not need to modify your Cargo.toml and this file will be compiled to a binary of the same name as the library.
The project contents:
% 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)
);
}
And execute it:
% cargo run -q
I'm using the library: Ok(3)
Flexible
If you wish to control the name of the binary or have multiple binaries, you can create multiple binary source files in src/bin and the rest of your library 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 in src/bin will be compiled to a binary of the same name.
The project contents:
% 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)
);
}
And execute it:
% cargo run --bin mybin -q
I'm using the library: Ok(3)
See also:
How can I specify which crate `cargo run` runs by default in the root of a Cargo workspace?

An alternate solution is to not 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.
Here, I create a binary project that includes a library inside of it, but there are many possible ways of organizing the code:
% tree the-binary
the-binary
├── Cargo.toml
├── src
│   └── main.rs
└── the-library
├── Cargo.toml
└── src
└── lib.rs
Cargo.toml
This uses the [workspace] key and depends on the library:
[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)
}
And execute it:
% cargo run -q
I'm using the library: Ok(3)
There are two big benefits to this scheme:
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.
The workspace prevents redundant builds of each component. If we run cargo build in both the the-library and the-binary directory, the library will not be built both times — it's shared between both projects.

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

Related

Why I cannot find the compiled wasm file?

I am new to the WASM world. I tried to create a really simple rust lib that only contains add function and wants to compile it to the .wasm. I already know that the wasm-pack can achieve this. But why the cargo build --target wasm32-unknown-unknown --release can not?
// Cargo.toml
[package]
name = "hello-wasm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
// lib.rs
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
The compiled target folder:
$ ls target/wasm32-unknown-unknown/release/
build/ deps/ examples/ incremental/ libhello_wasm.d libhello_wasm.rlib
Expected there is a file named hello-wasm.wasm.
With a lib.rs and no customization, you've defined a Rust library. By default, Cargo compiles libraries to .rlib files which can then be used as inputs when compiling crates that depend on this one.
In order to get a .wasm file, you must request a crate-type of cdylib in your configuration (and this is also standard when using wasm-pack):
[lib]
crate-type = ["cdylib"]
If you want the library to also be usable in tests and as a dependency, then you should specify the default mode "lib" too:
[lib]
crate-type = ["lib", "cdylib"]
(cdylib stands for "C dynamic library" — a WASM module is kind of like a dynamic library, and "C" should be understood as just meaning "native format, no Rust-specific conventions".)

How to export traits from a separate cargo workspace github repo

I am fairly new to rust and am trying to understand how best to create re-useable shared library components. I have a github repo named rust-libs that is setup as a cargo workspace. The project tree for this repo looks like this:
├── Cargo.lock
├── Cargo.toml
├── describable
│ ├── Cargo.toml
│ └── src
│ ├── describable.rs
│ └── lib.rs
└── health_check
├── Cargo.toml
└── src
├── health_check.rs
└── lib.rs
where the top-level Cargo.toml file contains:
[workspace]
members = [
"describable",
"health_check"
]
The Cargo.toml file in each member just defines the dependencies of that member and its version - e.g. describable/Cargo.toml:
[package]
name = "lib_describable"
version = "1.0.0"
authors = ["my-name <me#email.com>"]
edition = "2018"
[lib]
name = "lib_describable"
path = "src/lib.rs"
and health_check/Cargo.toml:
[package]
name = "lib_health_check"
version = "1.0.0"
authors = ["my-name <me#email.com>"]
edition = "2018"
[dependencies]
lib_describable = { path = "../describable" }
[lib]
name = "lib_health_check"
path = "src/lib.rs"
Note that I am naming the library with a lib_ prefix just to avoid any clashes with other rust libraries.
The lib.rs file in each workspace member just defines the public modules I want to export - e.g. describable/src/lib.rs:
pub mod describable;
The implementation for this is in describable/src/describable.rs:
pub trait Describable {
fn describe(&self) -> String;
}
The implementation in health_check/src/health_check.rs is:
use lib_describable::describable::Describable;
pub trait HealthCheckable: Describable {
fn check_health(&mut self) -> Result<(), String>;
}
When making any changes to a specific member, I always update its version number in its Cargo.toml file. CircleCI is used to automatically build this repo. If the build is successful, then the CircleCI job tags this repo with a multiple git tags (one per member in the workspace) and these tags are of the form <member-name>_<member-version-from-its-cargo-toml>_<git-short-commit-sha>, e.g. for the above two members it might tag the build with these tags:
describable_1.0.0_d2db9ff
health_check_1.0.0_d2db9ff
I then have a separate git repo that contains a rust binary that is built that makes use of these shared library members. The project refers to the shared library members in its Config.toml as follows:
...
[dependencies]
...
lib_describable = { git = "ssh://git#github.com/xxx/rust-libs.git", tag = "describable_1.0.0_d2db9ff" }
lib_health_check = { git = "ssh://git#github.com/xxx/rust-libs.git", tag = "health_check_1.0.0_d2db9ff" }
...
[[bin]]
name = "my-app"
path = "src/bin/main.rs"
xxx is just the obfuscated name of my github account. The file src/bin/main.rs in this project contains code of the form:
extern crate lib_describable;
extern crate lib_health_check;
use lib_describable::describable::Describable;
use lib_health_check::health_check::HealthCheckable;
pub trait MyDb: Describable + HealthCheckable {
// some functions defined here that are not important for this issue
}
pub struct MySpecialDb { ... }
impl Describable for MySpecialDb {
fn describe(&self) -> String {
// returns a description of this DB
}
}
impl HealthCheckable for MySpecialDb {
fn check_health(&mut self) -> Result<(), String> {
// performs some health check specific to this DB and returns either Ok(()) if its healthy or Err("...some error message...") if it is unhealthy
}
}
impl MyDb for MySpecialDb { ... }
The problem I am finding is that the rust compiler does not seem to like the line impl HealthCheckable for MySpecialDb and reports an error of the form:
the trait bound `...::MySpecialDb: lib_describable::describable::Describable` is not satisfied
the trait `lib_describable::describable::Describable` is not implemented for `...::MySpecialDb`
note: perhaps two different versions of crate `lib_describable` are being used?rustc(E0277)
Is there something obvious that I am doing wrong here to cause this error?
Unfortunately it seems this is not a supported configuration in Cargo as can be seen from the responses I got for the issue I raised here: https://github.com/rust-lang/cargo/issues/8956
I have therefore converted my Cargo workspace repo into a Cargo lib repo where all my libs share a single version now.

Unresolved import in Rust. Can't find crate [duplicate]

I have 4 files:
main.rs
mod bar;
fn main() {
let v = vec![1, 2, 3];
println!("Hello, world!");
}
lib.rs
pub mod foo;
pub mod bar;
foo.rs
pub fn say_foo() {
}
bar.rs
use crate::foo;
fn bar() {
foo::say_foo();
}
When I run cargo run I get an error saying:
error[E0432]: unresolved import `crate::foo`
--> src/bar.rs:1:5
|
1 | use crate::foo;
| ^^^^^^^^^^ no `foo` in the root
Could someone explain to me how to fix this? A bit more broadly: how does module lookup work when there's a main.rs and a lib.rs?
Edit: Adding mod foo to main.rs fixes the issue. But I don't understand this -- I was under the impression the lib.rs was the place that "exposed" all of my modules? Why do I have to declare the module in main.rs as well?
My Cargo.toml:
[package]
name = "hello-world"
version = "0.1.0"
authors = ["me#mgail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
Let's start from the beginning. Look at the Package Layout chapter in The Cargo Book. As you can see, your package can contain lot of stuff:
a binary (something you can run) or multiple binaries,
a single library (shared code),
example(s),
benchmark(s),
integration tests.
Package layout
Not all of the possibilities are listed here, just the binary / library combinations.
A binary
This is an example of a package with single binary. Entry point is the main function in the src/main.rs.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/main.rs:
fn main() {
println!("Hallo, Rust here!")
}
$ cargo run
Hallo, Rust here!
A library
This is an example of a package with a library. Libraries don't have entry points, you can't run them. They're used for functionality sharing.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/lib.rs:
pub fn foo() {
println!("Hallo, Rust library here!")
}
$ cargo run
error: a bin target must be available for `cargo run`
Do you see anything in the Cargo.toml file about a binary or a library? No. The reason is that I've followed the Package Layout and the cargo knows where to look for things.
A binary and a library
This is an example of a package with a binary and a library.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/lib.rs:
pub const GREETING: &'static str = "Hallo, Rust library here!";
src/main.rs:
use hallo::GREETING;
fn main() {
println!("{}", GREETING);
}
Same question, do you see anything in the Cargo.toml file about a binary or a library? No.
This package contains two things:
a binary (root src/main.rs, entry point src/main.rs::main),
a library (root src/lib.rs, shared code).
A library can be referenced from the binary via use hallo::... where the hallo is this package name (Cargo.toml -> [package] -> name).
Your problem
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
Same package layout
A library part
src/lib.rs:
pub mod bar;
pub mod foo;
src/foo.rs:
pub fn say_foo() {
println!("Foo");
}
src/bar.rs:
use crate::foo;
pub fn bar() {
foo::say_foo();
}
crate refers to src/lib.rs, because we're in the context of our library here.
Treat it as a standalone unit and refer to it via use hallo::...; from the outside world.
A binary part
src/main.rs:
use hallo::bar::bar;
fn main() {
bar();
}
Here we're just using our library.
Without a library
Same code, but lib.rs was renamed to utils.rs and (foo|bar).rs files were moved to the src/utils/ folder.
src/utils.rs:
pub mod bar;
pub mod foo;
src/utils/foo.rs:
pub fn say_foo() {
println!("Foo");
}
src/utils/bar.rs:
use super::foo;
// or use crate::utils::foo;
pub fn bar() {
foo::say_foo();
}
We can use crate here as well, but because we're in the context of our binary, the path differs.
src/main.rs:
use utils::bar::bar;
mod utils;
fn main() {
bar();
}
Here we just declared another module (utils) and we're using it.
Summary
Cargo.toml content:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
If there's a src/main.rs file, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[[bin]]
name = "hallo"
src = "src/main.rs"
If there's a src/lib.rs file, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[lib]
name = "hallo"
path = "src/lib.rs"
If there're both of them, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[[bin]]
name = "hallo"
path = "src/main.rs"
[lib]
name = "hallo"
path = "src/lib.rs"
Documentation
Package Layout
The Manifest Format
Managing Growing Projects with Packages, Crates, and Modules
In short the official Rust book has this to say:
If a package contains src/main.rs and src/lib.rs, it has two crates: a library and a binary, both with the same name as the package.
Furthermore the Rust reference says this:
crate resolves the path relative to the current crate
So there are actually two crates in your project, and to which crate the crate qualifier resolves to depends on where you call it.
Now in your code example, if you want things to compile you have to remove mod bar; from src/main.rs. Otherwise you'll be declaring that bar is a module within two crates.
After you remove that, then because in src/lib.rs you had:
pub mod foo;
pub mod bar;
bar would now be a module within src/lib.rs's crate, so the crate qualifier in bar.rs would then refer to src/lib.rs's hello-world crate, which is what you want.
One more thing, if you wanted to access items that are exposed in src/lib.rs from src/main.rs, you have to do as #zrzka said, which is to name the name of the crate that both src/lib.rs and src/main.rs share. For example, in your project which is named hello-world:
use hello_world::foo;
fn main() {
foo::say_foo();
}
is how you import the foo module declared in src/lib.rs into src/main.rs.
However it does appear that the importing behavior doesn't work the other way. I.e. if you declare some public module in src/main.rs, you can't import it into the src/lib.rs crate even when you specify the name of the crate. I couldn't find documentation describing this behavior but by testing it in Rust 1.37.0, it does appear to be the case.
The lib.rs and main.rs files are two independent entry points for your package.
When you use cargo run (or build the binary and run it explicitly), the entry point to be used is main.rs, and the crate keyword refer to the binary crate. It doesn't even have to know that there is something in lib.rs: the binary will treat the library as it would any other external crate, and it must be imported, through extern crate hello_world or, for example, use hello_world::foo.
When you import the library, however, the entry point is lib.rs, and the crate is the library crate. In this case, yes, all that you've added to lib.rs is exposed to the whole crate.
The usual worksflow in this case is to make the binary something like a thin wrapper around the library - in some extreme cases the main.rs would only contain something like
fn main() {
library::main();
}
and the whole logic (and all the project structure) goes into the library crate. One of the reasons is exactly what you've run into: the possible confusion whether this concrete module is imported in each crate in the package.
One more thing: mod defines a new module in your crate, whether a binary crate or library crate; while use only brings the module into the current scope.
In your example, use crate::foo in bar.rs tries to bring a module named foo under crate root into scope. But because there is no mod foo in main.rs, the foo module is not part of the binary crate.

How do I use a custom namespaced derive-macro attribute on a type instead of inside the type?

I'd like to create a custom derive macro that uses the new namespaced attribute syntax: example::attr. I've been able to get this to work with attributes within the type (on a struct field or an enum variant, for example), but not when applied to the type itself.
src/main.rs
use repro_derive::Example;
#[derive(Example)]
#[example::attr] // Does not work
struct Demo {
#[example::attr] // Works
field: i32,
}
fn main() {}
The procedural macro itself does nothing, other than to declare that example::attr is a valid attribute.
repro-derive/src/lib.rs
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(Example, attributes(example::attr))]
pub fn example_derive(_input: TokenStream) -> TokenStream {
TokenStream::new()
}
Compiling yields:
error[E0433]: failed to resolve: use of undeclared type or module `example`
--> src/main.rs:4:3
|
4 | #[example::attr]
| ^^^^^^^ use of undeclared type or module `example`
Switching to a non-namespaced form of the attribute (example_attr) works fine.
I'm using Rust 1.32.0. The project layout is
$ tree
.
├── Cargo.lock
├── Cargo.toml
├── repro-derive
│   ├── Cargo.toml
│   └── src
│   └── lib.rs
└── src
└── main.rs
Cargo.toml
$ cat Cargo.toml
[package]
name = "repro"
version = "0.1.0"
authors = ["Author"]
edition = "2018"
[dependencies]
repro-derive = { path = "repro-derive" }
repro-derive/Cargo.toml
[package]
name = "repro-derive"
version = "0.1.0"
authors = ["Author"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
The namespace declared in the proc_macro_derive attribute is entirely ignored, and this is a known bug. Because of this bug, the following code can be compiled, though it shouldn't be.
#[derive(Example)]
#[attr] // Works (but shouldn't)
struct Demo {
#[lolwut::attr] // Works (but shouldn't)
field: i32,
}
Until the bug is fixed, you should keep using the non-namespaced form (example_attr).
Also, according to this bug report, as of Rust 1.33.0 there is no way to achieve what OP wants via proc-macros, and how to allow #[example::attr] to work is still under design.

How can I use shared logic from different files?

Having shared logic in:
// euler/shared/lib.rs
pub fn foo() {
println!("shared::foo()");
}
How can I use it from different files:
// euler/001/main.rs
use super::shared; // error: unresolved import `super::shared`
fn main() {
shared::foo(); // how to access it?
}
// euler/002/main.rs
use super::shared; // error: unresolved import `super::shared`
fn main() {
shared::foo(); // how to access it?
}
mdup's answer is correct, but I'd encourage you to use Cargo, Rust's package manager. It will do two very important things for you here:
Set up the correct command line arguments to rustc.
Automatically rebuild the dependent libraries when they change.
Use cargo new shared and cargo new --bin euler-001 to generate the right directory structure. Move your shared code to shared/src/lib.rs and your binaries to euler-001/src/main.rs:
.
├── euler-001
│   ├── Cargo.toml
│   └── src
│   └── main.rs
└── shared
├── Cargo.toml
└── src
└── lib.rs
Then, edit euler-001/Cargo.toml and add the dependencies section:
[dependencies.shared]
path = "../shared"
And tweak your main.rs to know about the crate:
extern crate shared;
fn main() {
shared::foo();
}
Now, you can simply type cargo run in the euler-001 directory:
$ cargo run
Compiling shared v0.1.0 (file:///private/tmp/play/euler-001)
Compiling euler-001 v0.1.0 (file:///private/tmp/play/euler-001)
Running `target/debug/euler-001`
shared::foo()
Note that you don't have to remember command line arguments and things are compiled for you! Having a built-in package manager is great!
One solution is to create a library out of the shared code. This will allow you to use an extern crate declaration.
// euler/shared/shared.rs
pub fn foo() {
println!("shared::foo()");
}
To compile the lib:
$ cd euler/shared
$ rustc --crate-type=lib shared.rs
$ ls -l libshared.rlib
-rw-r--r-- 1 mdup wheel 6758 May 17 14:38 libshared.rlib
Here is how you use it in "client" code:
// euler/001/main.rs
extern crate shared;
fn main() {
shared::foo();
}
The compile the client:
$ cd euler/001
$ rustc -L ../shared main.rs
$ ls -l main
-rwxr-xr-x 1 mdup wheel 291420 May 17 14:42 main
$ ./main
shared::foo()
More info in Rust By Example, section "Crates", pages "Library" and "extern crate".

Resources