Rust tip and trick — organize project 1 Link to heading

If you want to start a Rust project, you might be tempted to run the cargo command like this:

$ cargo new my_awesome_project
  Created binary (application) `my_awesome_project` package

This creates a simple binary boilerplate project with a single source file src/main.rs.

$ tree my_awesome_project/
my_awesome_project/
|-- Cargo.toml
`-- src
    `-- main.rs

This is probably fine if you are just learning Rust or experimenting with some ideas. However, for a more serious project, this is not an ideal structure. A better way to organize the project is to separate the source files into a binary and a library. This way, you can reuse the library code in different binaries or even publish it as a crate. To achieve this, you need a slightly different cargo command:

$ cargo new --lib my_awesome_project
  Created library `my_awesome_project` package

This creates a library project without any binary. To add binary files, you simply need to manually create a src/bin folder and put any Rust source file in there. Cargo will automatically recognize them as binaries and compile them accordingly.

$ cd my_awesome_project
$ mkdir src/bin
$ echo 'fn main() { println!("binary1"); }' > src/bin/binary1.rs
$ echo 'fn main() { println!("binary2"); }' > src/bin/binary2.rs
$ tree
.
|-- Cargo.lock
|-- Cargo.toml
`-- src
    |-- bin
    |   |-- binary1.rs
    |   `-- binary2.rs
    `-- lib.rs

$ cargo build
   Compiling my_awesome_project v0.1.0 (my_awesome_project)
    Finished dev [unoptimized + debuginfo] target(s) in 0.27s

$ cargo run --bin binary1
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/binary1`
binary1

$ cargo run --bin binary2
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/binary2`
binary2

So far, we have created a library project and added binary files, but haven’t actually linked them yet. In order for the binary files to access library functions, we just need to declare use my_awesome_project in the binary source files. For example, the boilerplate src/lib.rs has add() function:

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

To use this function from the binary files, we modify, say src/bin/binary1.rs as follows:

use my_awesome_project::add;

fn main() {
    println!("1 + 2 = {}", add(1, 2));
}

Now, we can run the binary1 file and confirm that it can indeed import from the library

$ cargo run --bin binary1
   Compiling my_awesome_project v0.1.0 (my_awesome_project)
    Finished dev [unoptimized + debuginfo] target(s) in 0.13s
     Running `target/debug/binary1`
1 + 2 = 3

There exist more advanced methods to separate the binary and library source files, such as using workspace. I will cover that in a later story, so stay tuned!