Creating a library crate to support a Rust binary crate in the same package

InstructorChris Biscardi

Share this video with your friends

Send Tweet

In our subcommand match we'll add a new function called write to handle our Write subcommand. We'll pass it the argument title and we'll "import" it from a library called digital_garden.

use digital_garden::write;

fn main() -> Result<()> {
    color_eyre::install()?;

    let opt = Opt::from_args();
    dbg!(&opt);
    match opt.cmd {
        Command::Write { title } => write(title),
    }
}

To create this library, add a new section in Cargo.toml. We can give our library a name and set which file to treat as the entrypoint. src/lib.rs is the default file for a library crate, but we can also be explicit about what we want to happen.

[lib]
name = "digital_garden"
path = "src/lib.rs"

This leaves us with two crates, a binary crate, of which we can have any amount, and a library crate, of which we can only have 0 or 1.

We'll create the src/lib.rs file with the following contents.

mod write;

pub use write::write;

Rust's module system is explicit, so when we write mod write, we are saying that the library digital_garden has a module write, such that the path to the module is digital_garden::write.

In our case digital_garden::write corresponds to a file at src/write.rs.

Secondly, the pub use write::write says that we have a function called write in the module called write and that the function will be exposed from lib publicly, so people who have the digital_garden library installed will be able to use the write function as digital_garden::write.

In JavaScript the closest analogue is exporting a function from another file that exports it.

export { write } from './write.js';

By using use, we're shortening the public path to the write function. A private module "write" with a function "write" also exists at digital_garden::write::write. If you try to use this path, Rust will tell you there is a private module there.

Finally, in src/write.rs, we can write a public function that matches the return type of our main function, since we're using it as the return value from our match function.

We'll accept an Option<String> as an argument as well, since that's what we expect from the cli arguments.

use color_eyre::Result;

pub fn write(_title: Option<String>) -> Result<()> {
    todo!()
}

The purpose of creating this module path is because we know that we're going to set up more modules for each subcommand in the future, and we can control both the placement of our code on disk, as well as the path users will interact with to use our module.