R1: Cargo Flashcards
What is Cargo?
Cargo is Rust’s build system and package manager. It can: build your code, download the libraries your code depends on, and build those libraries.
Cmd: cargo new <project name>
We can create a project. You’ll see that Cargo has generated two files and one directory for us: a Cargo.toml file and a src directory with a main.rs file inside.
Cmd: cargo new --vcs=git hello_cargo
Same as cargo new <project name>
but overrides current git config files.
What does the Cargo.toml contain?
This file is in the TOML (Tom’s Obvious, Minimal Language) format, which is Cargo’s configuration format.
Example:
[package]
name = “hello_cargo”
version = “0.1.0”
edition = “2021”
See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
What is [package] in Cargo.toml?
The first line, [package], is a section heading that indicates that the following statements are configuring a package.
What is [dependencies] in Cargo.toml?
Is the start of a section for you to list any of your project’s dependencies. In Rust, packages of code are referred to as crates.
Cmd: cargo build
We can build a project
Cmd: cargo run
We can build and run a project in one step
Cmd: cargo check
We can build a project without producing a binary to check for errors using
target/debug
directory
Instead of saving the result of the build in the same directory as our code, Cargo stores it in the target/debug directory.
Cmd: cargo build --release
?
When your project is finally ready for release, you can use cargo build –release to compile it with optimizations. This command will create an executable in target/release instead of target/debug.
Cmd: cargo build --release
?
When your project is finally ready for release, you can use cargo build –release to compile it with optimizations. This command will create an executable in target/release instead of target/debug.
module system
Rust has a number of features that allow you to manage your code’s organization, including which details are exposed, which details are private, and what names are in each scope in your programs.
These features include:
- Packages: A Cargo feature that lets you build, test, and share crates
- Crates: A tree of modules that produces a library or executable
- Modules and use: Let you control the organization, scope, and privacy of paths
- Paths: A way of naming an item, such as a struct, function, or module
crate
A crate is the smallest amount of code that the Rust compiler considers at a time. A crate can come in one of two forms:
- Binary crates: are programs you can compile to an executable that you can run, such as a command-line program or a server.
- 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.
crate root
The crate root is a source file that the Rust compiler starts from and makes up the root module of your crate (usually src/lib.rs for a library crate or src/main.rs for a binary 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.
Declaring submodules
Declaring modules: In the crate root file, you can declare new modules; say, you declare a “garden” module with mod garden;.
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 modules
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.
use keyword
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.
backyard
Here we create a binary crate named backyard that illustrates these rules. The crate’s directory, also named backyard, contains these files and directories:
backyard ├── Cargo.lock ├── Cargo.toml └── src ├── garden │ └── vegetables.rs ├── garden.rs └── main.rs
Example of modules use
``` 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 {}
~~~
Grouping Related Code in Modules
Examples of fs tree:
~~~
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
Examples of module:
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() {} } } ~~~
absolute path
- 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.
Emod front_of_house { mod hosting { fn add_to_waitlist() {} } } pub fn eat_at_restaurant() { // Absolute path crate::front_of_house::hosting::add_to_waitlist(); }
relative path
- A relative path: starts from the current module and uses self, super, or an identifier in the current module.
Emod front_of_house { mod hosting { fn add_to_waitlist() {} } } pub fn eat_at_restaurant() { // Relative path front_of_house::hosting::add_to_waitlist();
Starting Relative Paths with super
fn deliver_order() {}
mod back_of_house {
fn fix_incorrect_order() {
cook_order();
super::deliver_order();
}
fn cook_order() {} }
Making Structs and Enums Public
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"); }
Providing New Names with the as Keyword
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {
// –snip–
}
fn function2() -> IoResult<()> {
// –snip–
}
Using External Packages
To use rand (External Packages) in our project, we added this line to Cargo.toml:
Filename: Cargo.toml
~~~
rand = “0.8.5”
~~~
Adding rand as a dependency in Cargo.toml tells Cargo to download the rand package and any dependencies from crates.io and make rand available to our project.
use rand::Rng; fn main() { let secret_number = rand::thread_rng().gen_range(1..=100); }
Using Nested Paths to Clean Up Large use Lists
Before:
// –snip–
use std::cmp::Ordering;
use std::io;
// –snip–
After:
// –snip–
use std::{cmp::Ordering, io};
// –snip–
Before:
// –snip–
use std::io;
use std::io::Write;
// –snip–
After:
use std::io::{self, Write};
// –snip–
The 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::*;