Cargo is Rustโ€™s build system and package manager.

To create a new project with Cargo:

cargo new hello_cargo

It will create a directory hello_cargo with the following structure:

.
โ”œโ”€โ”€ Cargo.toml
โ”œโ”€โ”€ .gitignore
โ””โ”€โ”€ src
    โ””โ”€โ”€ main.rs

Git repository will be initialized as well. If initialized in a directory with an existing git config, it wouldnโ€™t override it by default.

In Rust, packages of code are referred to as crates.

Cargo expects your source files to live inside the src directory. The top-level project directory is just for README files, license information, configuration files, and anything else not related to your code.

cargo build
cargo build --release # build for production
 
# build + run
cargo run
 
# validate that code compiles
cargo check
 
cargo update # update dependencies
 
cargo doc --open # open local docs for all your installed dependencies

Cargo understands Semantic Versioning (sometimes called SemVer), which is a standard for writing version numbers. The specifier 0.8.5 is actually shorthand for ^0.8.5, which means any version that is at least 0.8.5 but below 0.9.0.

Crates.io is where people in the Rust ecosystem post their open source Rust projects for others to use.

Crate

A crate is the smallest amount of code that the Rust compiler considers at a time. Even if you run rustc rather than cargo and pass a single source code file , the compiler considers that file to be a crate.

Crates can contain modules, and the modules may be defined in other files that get compiled with the crate.

A crate can come in one of two forms: a binary crate or a library crate.

  • Binary crates are programs you can compile to an executable that you can run, such as a command-line program or a server. Each must have a function called main that defines what happens when the executable runs.
  • Library crates donโ€™t have a main function, and they donโ€™t compile to an executable. Instead, they define functionality intended to be shared with multiple projects.

Most of the time when Rustaceans say โ€œcrateโ€, they mean library crate, and they use โ€œcrateโ€ interchangeably with the general programming concept of a โ€œlibraryโ€.

The crate root is a source file that the Rust compiler starts from and makes up the root module of your crate.

Package

A package is a bundle of one or more crates that provides a set of functionality. A package contains a Cargo.toml file that describes how to build those crates.

A package can contain as many binary crates as you like, but at most only one library crate. A package must contain at least one crate, whether thatโ€™s a library or binary crate.

Cargo follows a convention that src/main.rs is the crate root of a binary crate with the same name as the package.

Likewise, Cargo knows that if the package directory contains src/lib.rs, the package contains a library crate with the same name as the package, and src/lib.rs is its crate root.

Cargo passes the crate root files to rustc to build the library or binary.

If a package contains src/main.rs and src/lib.rs, it has two crates: a binary and a library, both with the same name as the package.

A package can have multiple binary crates by placing files in the src/bin directory: each file will be a separate binary crate.

Modules

Start from the crate root: When compiling a crate, the compiler first looks in the crate root file (usually src/lib.rs for a library crate or src/main.rs for a binary crate) for code to compile.

Declaring modules: In the crate root file, you can declare new modules; say, you declare a โ€œgardenโ€ module with mod garden;. The compiler will look for the moduleโ€™s code in these places: - Inline, within curly brackets that replace the semicolon following mod garden - In the file src/garden.rs - In the file src/garden/mod.rs

Declaring submodules: In any file other than the crate root, you can declare submodules. For example, you might declare mod vegetables; in src/garden.rs. The compiler will look for the submoduleโ€™s code within the directory named for the parent module in these places: - Inline, directly following mod vegetables, within curly brackets instead of the semicolon - In the file src/garden/vegetables.rs - In the file src/garden/vegetables/mod.rs

Paths to code in modules: Once a module is part of your crate, you can refer to code in that module from anywhere else in that same crate, as long as the privacy rules allow, using the path to the code. For example, an Asparagus type in the garden vegetables module would be found at crate::garden::vegetables::Asparagus.

Private vs public: Code within a module is private from its parent modules by default. To make a module public, declare it with pub mod instead of mod. To make items within a public module public as well, use pub before their declarations.

The use keyword: Within a scope, the use keyword creates shortcuts to items to reduce repetition of long paths. In any scope that can refer to crate::garden::vegetables::Asparagus, you can create a shortcut with use crate::garden::vegetables::Asparagus; and from then on you only need to write Asparagus to make use of that type in the scope.

Example:

backyard
โ”œโ”€โ”€ Cargo.lock
โ”œโ”€โ”€ Cargo.toml
โ””โ”€โ”€ src
    โ”œโ”€โ”€ garden
    โ”‚   โ””โ”€โ”€ vegetables.rs
    โ”œโ”€โ”€ garden.rs
    โ””โ”€โ”€ main.rs
// src/main.rs
use crate::garden::vegetables::Asparagus;
 
pub mod garden;
 
fn main() {
    let plant = Asparagus {};
    println!("I'm growing {:?}!", plant);
}
 
// src/garden.rs
pub mod vegetables;
 
// src/garden/vegetables.rs
#[derive(Debug)]
pub struct Asparagus {}

src/main.rs and src/lib.rs are called crate roots. The reason for their name is that the contents of either of these two files form a module named crate at the root of the crateโ€™s module structure, known as the module tree. For the code:

// src/lib.rs
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
 
        fn seat_at_table() {}
    }
 
    mod serving {
        fn take_order() {}
 
        fn serve_order() {}
 
        fn take_payment() {}
    }
}

The module tree will look like this:

crate
 โ””โ”€โ”€ front_of_house
     โ”œโ”€โ”€ hosting
     โ”‚   โ”œโ”€โ”€ add_to_waitlist
     โ”‚   โ””โ”€โ”€ seat_at_table
     โ””โ”€โ”€ serving
         โ”œโ”€โ”€ take_order
         โ”œโ”€โ”€ serve_order
         โ””โ”€โ”€ take_payment

Notice that the entire module tree is rooted under the implicit module named crate.

Each item in a module tree has a path. A path can take two forms:

  • An absolute path is the full path starting from a crate root; for code from an external crate, the absolute path begins with the crate name, and for code from the current crate, it starts with the literal crate.
  • A relative path starts from the current module and uses self, super, or an identifier in the current module. Both absolute and relative paths are followed by one or more identifiers separated by double colons ::.
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
 
pub fn eat_at_restaurant() {
    // Absolute path
    crate::front_of_house::hosting::add_to_waitlist();
 
    // Relative path
    front_of_house::hosting::add_to_waitlist();
}

Our preference in general is to specify absolute paths because itโ€™s more likely weโ€™ll want to move code definitions and item calls independently of each other. https://doc.rust-lang.org/stable/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html

Privacy

In Rust, all items (functions, methods, structs, enums, modules, and constants) are private to parent modules by default.

Items in a parent module canโ€™t use the private items inside child modules, but items in child modules can use the items in their ancestor modules. This is because child modules wrap and hide their implementation details, but the child modules can see the context in which theyโ€™re defined.

Rust chose to have the module system function this way so that hiding inner implementation details is the default. That way, you know which parts of the inner code you can change without breaking outer code.

Rust does give you the option to expose inner parts of child modulesโ€™ code to outer ancestor modules by using the pub keyword to make an item public.

Making the module public doesnโ€™t make its contents public. The pub keyword on a module only lets code in its ancestor modules refer to it, not access its inner code.

Siblings can access each other. For example, in the example above eat_at_restaurant function is defined in the same module as front_of_house (that way they are siblings), we can refer to front_of_house from eat_at_restaurant

Structs and enums

If we use pub before a struct definition, we make the struct public, but the structโ€™s fields will still be private. We can make each field public or not on a case-by-case basis.

mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }
 
    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}
 
pub fn eat_at_restaurant() {
    // Order a breakfast in the summer with Rye toast
    let mut meal = back_of_house::Breakfast::summer("Rye");
    // Change our mind about what bread we'd like
    meal.toast = String::from("Wheat");
    println!("I'd like {} toast please", meal.toast);
 
    // The next line won't compile if we uncomment it; we're not allowed
    // to see or modify the seasonal fruit that comes with the meal
    // meal.seasonal_fruit = String::from("blueberries");
}

If struct has a private field, we need to provide a public method to construct it, otherwise the client wouldnโ€™t be able to instantiate a struct.

In contrast, if we make an enum public, all of its variants are then public. We only need the pub before the enum keyword.

    pub enum Appetizer {
        Soup,
        Salad,
    }
}
 
pub fn eat_at_restaurant() {
    let order1 = back_of_house::Appetizer::Soup;
    let order2 = back_of_house::Appetizer::Salad;
}

Enums arenโ€™t very useful unless their variants are public; it would be annoying to have to annotate all enum variants with pub in every case, so the default for enum variants is to be public. Structs are often useful without their fields being public, so struct fields follow the general rule of everything being private by default unless annotated with pub.

Best Practices for Packages with a Binary and a Library

We mentioned a package can contain both a src/main.rs binary crate root as well as a src/lib.rs library crate root, and both crates will have the package name by default. Typically, packages with this pattern of containing both a library and a binary crate will have just enough code in the binary crate to start an executable that calls code with the library crate. This lets other projects benefit from the most functionality that the package provides, because the library crateโ€™s code can be shared.

The module tree should be defined in src/lib.rs. Then, any public items can be used in the binary crate by starting paths with the name of the package. The binary crate becomes a user of the library crate just like a completely external crate would use the library crate: it can only use the public API. This helps you design a good API; not only are you the author, youโ€™re also a client!

super in relative path

We can construct relative paths that begin in the parent module, rather than the current module or the crate root, by using super at the start of the path.

Using super allows us to reference an item that we know is in the parent module, which can make rearranging the module tree easier when the module is closely related to the parent, but the parent might be moved elsewhere in the module tree someday.

fn deliver_order() {}
 
mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        super::deliver_order();
    }
 
    fn cook_order() {}
}

use keyword

We can create a shortcut to a path with the use keyword once, and then use the shorter name everywhere else in the scope.

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
 
use crate::front_of_house::hosting;
 
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

Paths brought into scope with use also check privacy, like any other paths.

use only creates the shortcut for the particular scope in which the use occurs. Because of that this code wonโ€™t compile:

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
 
use crate::front_of_house::hosting; // warning: unused import: `crate::front_of_house::hosting`
 
mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist(); // failed to resolve: use of undeclared crate or module `hosting`
    }
}

Idiomatic way to bring a function into a scope is to bring it parent, so it would be obvious what is called and where is it:

// Not idiomatic
use crate::front_of_house::hosting::add_to_waitlist;
 
pub fn eat_at_restaurant() {
    add_to_waitlist();
}
 
// Idiomatic
use crate::front_of_house::hosting;
 
mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }
}

On the other hand, when bringing in structs, enums, and other items with use, itโ€™s idiomatic to specify the full path.

use std::collections::HashMap;
 
fn main() {
    let mut map = HashMap::new();
    map.insert(1, 2);
}

Thereโ€™s no strong reason behind this idiom: itโ€™s just the convention that has emerged, and folks have gotten used to reading and writing Rust code this way.

The exception to this idiom is if weโ€™re bringing two items with the same name into scope with use statements.

use std::fmt;
use std::io;
 
fn function1() -> fmt::Result {
    // --snip--
}
 
fn function2() -> io::Result<()> {
    // --snip--
}

You can also provide a new name (alias) with the as keyword:

use std::fmt::Result;
use std::io::Result as IoResult;
 
fn function1() -> Result {
    // --snip--
}
 
fn function2() -> IoResult<()> {
    // --snip--
}

When we bring a name into scope with the use keyword, the name available in the new scope is private. To enable the code that calls our code to refer to that name as if it had been defined in that codeโ€™s scope, we can combine pub and use. This technique is called re-exporting because weโ€™re bringing an item into scope but also making that item available for others to bring into their scope.

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
 
pub use crate::front_of_house::hosting; // With this external code can now use the path `restaurant::hosting::add_to_waitlist()`
 
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

Re-exporting is useful when the internal structure of your code is different from how programmers calling your code would think about the domain. With pub use, we can write our code with one structure but expose a different structure. Doing so makes our library well organized for programmers working on the library and programmers calling the library.

Nested paths

use std::cmp::Ordering;
use std::io;
// same as above
use std::{cmp::Ordering, io};

self can be used to specify a current path:

use std::io;
use std::io::Write;
// same as above
use std::io::{self, Write};

Glob operator

If we want to bring all public items defined in a path into scope, we can specify that path followed by the * glob operator:

use std::collections::*;

Be careful when using the glob operator! Glob can make it harder to tell what names are in scope and where a name used in your program was defined.

The glob operator is often used when testing to bring everything under test into the tests module.

The glob operator is also sometimes used as part of the prelude pattern: see the standard library documentation for more information on that pattern.

Separating modules into different files

To import a module:

mod front_of_house;

Note that you only need to load a file using a mod declaration once in your module tree. Once the compiler knows the file is part of the project (and knows where in the module tree the code resides because of where youโ€™ve put the mod statement), other files in your project should refer to the loaded fileโ€™s code using a path to where it was declared. In other words, mod is not an โ€œincludeโ€ operation that you may have seen in other programming languages.

So far weโ€™ve covered the most idiomatic file paths the Rust compiler uses, but Rust also supports an older style of file path. For a module named front_of_house declared in the crate root, the compiler will look for the moduleโ€™s code in:

  • src/front_of_house.rs (what we covered)
  • src/front_of_house/mod.rs (older style, still supported path)

For a module named hosting that is a submodule of front_of_house, the compiler will look for the moduleโ€™s code in:

  • src/front_of_house/hosting.rs (what we covered)
  • src/front_of_house/hosting/mod.rs (older style, still supported path)

If you use both styles for the same module, youโ€™ll get a compiler error. Using a mix of both styles for different modules in the same project is allowed, but might be confusing for people navigating your project.

The main downside to the style that uses files named mod.rs is that your project can end up with many files named mod.rs, which can get confusing when you have them open in your editor at the same time.