Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

A Rust project is made of:

It is organized as a hierarchy of the following elements:

  1. Package: A Cargo project containing one or more crates.

  2. Crate: What are defined and distributed by packages and downloadable from crates.io.

  3. Module: A group of one or more code files.

  4. Path: The exact identification of a code element inside a file.

The following chapters present in more details those definitions.

26.1.1Package

When we start a new project/package with the cargo command-line tool, we have only the choice between two options:

26.1.1.1Binary package

With the following command, we create a new project named foo, containing a single binary crate:

    Creating binary (application) `foo` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

A Rust package is a folder with at its root:

Our foo package contains only a Cargo.toml file and one source file src/main.rs:

.
├── Cargo.toml
└── src
    └── main.rs

A package is defined by its Cargo.toml file. It contains (among other things):

Here is the content of your Cargo.toml file:

[package]
name = "foo"
version = "0.1.0"
edition = "2024"

[dependencies]

The content of the main.rs is simply a single main() function:

fn main() {
    println!("Hello, world!");
}

The default generated project compiles and runs:

   Compiling foo v0.1.0 (/tmp/tmp.5gStzYSnxv/foo)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/foo`
Hello, world!

26.1.1.2Library package

With the --lib argument, we define a library crate:

    Creating library `foo2` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

We get a lib.rs inside the src folder:

.
├── Cargo.toml
└── src
    └── lib.rs

The content of the Cargo.toml file still conveys no information on the crate type:

[package]
name = "foo2"
version = "0.1.0"
edition = "2024"

[dependencies]

The lib.rs file contains a public function example and its test function:

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

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}

The default generated project compiles and passes its test:

   Compiling foo2 v0.1.0 (/tmp/tmp.5gStzYSnxv/foo/foo2)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s
     Running unittests src/lib.rs (target/debug/deps/foo2-0f55e11fb220cf29)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests foo2

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

26.1.2Crate

By default a package contains only one crate, described inside the Cargo.toml file and whose code is stored inside the src folder. Multiple crates are possible in a project, see Multiple crates.

A crate contains zero or more modules defined hierarchically inside the src folder.

26.1.3Module

Modules are namespaces that regroup related code. They are part of a crate, and can be declared:

26.1.3.1Internal module

An internal module, is a module defined using the mod keyword. Here is the module math in which we define a single function add() (the following examples are pure Rust snippets, not executed under the bash kernel of this page):

mod math {
  pub fn add(a: i16, b: i16) -> i16 {
    a + b
  }
}

To call the add() function of the math module, we need to specify its path:

math::add(2, 6)

26.1.3.2File module

A module can be defined using a file, in which case the whole file is dedicated to the module’s content and the file name is the module’s name.

Let us create a new binary project to illustrate that:

    Creating binary (application) `foo4` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

We create the code file math.rs that we will use as a module. We define inside a function add():

pub fn add(a: i16, b: i16) -> i16 {
  a + b
}

Now we change the main.rs file so that it contains a main() function that calls the math::add() function. Note the declaration of the module math using the mod keyword. This declaration tells the compiler to look for a file math.rs inside the same folder as the main.rs file and to load it as a module named math:

mod math;

fn main () {
  println!("{}", math::add(2, 6));
}

The program compiles & runs:

   Compiling foo4 v0.1.0 (/tmp/tmp.5gStzYSnxv/foo/foo2/foo4)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/foo4`
8

26.1.4Path

The exact location of an item into a crate is called a path.

If we need to access an item inside an external crate, we use an absolute path. Cargo will find the location of the needed crate by looking into its installation folders. In the following example, to access the from_millis() function inside the std crate, we use the full path of the item:

let t = std::time::Duration::from_millis(700);
t

When referring a crate’s item from inside a crate we may use relative or absolute path.

For instance, in the following math module defined inside the current file, the double() function can access the add() function from the arithmetic sub-module relatively. We can use a relative path to access it:

mod math {

  pub mod arithmetic {
    pub fn add(a: i16, b: i16) -> i16 {
      a + b
    }
  }

  pub fn double(n: i16) -> i16 {
    arithmetic::add(n, n)
  }
}

math::double(5)

26.1.5Use keyword

The use keyword allows to avoid repeating a full absolute localisation of a module or an item’s module. It is especially useful with modules defined inside a deep hierarchy.

For instance, the std::time::Duration::from_millis() function is a bit long name. use can help by letting us access directly the Duration structure:

use std::time::Duration;

let t = Duration::from_millis(300);
t

We may also rename the Duration structure:

use std::time::Duration as Dur;

let t = Dur::from_millis(300);
t

It is also possible to access directly the whole public content of a module using the * wildcard, like in the following example:

use std::collections::*;

let lst = LinkedList::from([1, 2, 3]);
lst

However, a better practice is to name explicitly each item we need to access, possibly using curly braces to group multiple items:

use std::collections::{LinkedList, VecDeque};

let lst = LinkedList::from([1, 2, 3]);
let deq = VecDeque::from([-1, 0, 1]);
(lst, deq)