cargo and crates.io
Customizing builds with release profiles
Cargo has two main profiles:
- the
dev
profile- used when running
cargo build
- used when running
- the
release
profile- used when running
cargo build --release
- used when running
# default settings
# `opt-level` setting controls the number of optimizations Rust
# will apply to the code, with a range of 0 to 3
[profile.dev]
opt-level = 0
[profile.release]
opt-level = 3
Full list of configuration options and defaults for each profile: https://doc.rust-lang.org/cargo/reference/profiles.html
Publishing a crate to crates.io
Documentation comments use three slashes ///
instead of two and
support markdown notation for formatting the text.
cargo doc --open
will build the HTML of your current crate’s
documentation.
Commonly used sections
- panics: scenarios in which the function being documented could panic
- errors: if the function returns a
Result
, describing the kinds of errors that might occur and what conditions might cause those errors to be returned can be helpful to callers so they can write code to handle the different kinds of errors in different ways - safety: if the function is
unsafe
to call, there should be a section explaining why the function is unsafe and covering the invariants that the function expects callers to uphold.
Documentation comments as tests
Adding example code blocks in your documentation comments can help
demonstrate how to use your library. cargo test
will run the code
examples in your documentation as tests!
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
Commenting contained items
Another styl of doc comment, //!
adds documentation to the item
that contains the comments rather than adding documentation to the
items following the comments. Typically used inside the crate root
file (src/lib.rs
by convention) or inside a module to document
the crate or the module as a whole.
//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain
//! calculations more convenient.
/// Adds one to the number given.
// --snip--
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
Documentation comments within items are useful for describing crates and modules especially. Use them to explain the overall purpose of the container to help your users understand the crate’s organization.
Exporting a convenient public API with pub
use
//! # Art
//!
//! A library for modeling artistic concepts.
pub mod kinds {
/// The primary colors according to the RYB color model.
pub enum PrimaryColor {
Red,
Yellow,
Blue,
}
/// The secondary colors according to the RYB color model.
pub enum SecondaryColor {
Orange,
Green,
Purple,
}
}
pub mod utils {
use crate::kinds::*;
/// Combines two primary colors in equal amounts to create
/// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
// --snip--
unimplemented!();
}
}
// a crate using the `art` crate's items with its internal
// strucutre exported => not good
use art::kinds::PrimaryColor;
use art::utils::mix;
fn main() {
let red = PrimaryColor::Red;
let yellow = PrimaryColor::Yellow;
mix(red, yellow);
}
// adding `pub use` statements to re-export items
pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;
pub mod kinds {
// --snip--
}
// a program using the re-exported items from the `art` crate
// no internal structure exposed => better
use art::mix;
use art::PrimaryColor;
fn main() {
let red = PrimaryColor::Red;
let yellow = PrimaryColor::Yellow;
mix(red, yellow);
}
Setting up a crates.io accounts
To create an account, visite https://crates.io and link to a Github account.
Then login with:
# api token stored locally in ~/.cargo/credentials
cargo login foobar
Adding metadata to a new crate
You can add metadata in the [package]
section in Cargo.toml
file.
[package]
name = "guessing_game"
version = "0.1.0"
edition = "2018"
description = "A fun game where you guess what number the computer has chosen."
license = "MIT OR Apache-2.0"
Publishing in crates.io
cargo publish
When you’ve made changes to your crate and are ready to release a new version, you change the version value specified in your Cargo.toml file and republish. Use the Semantic Versioning rules to decide what an appropriate next version number is based on the kinds of changes you’ve made. Then run cargo publish to upload the new version.
# removing a versions
cargo yank --vers 1.0.1
# undo a yank
cargo yank --vers 1.0.1 --undo
Cargo workspaces
A workspace is a set of packages that share the same Cargo.lock
and output directory.
$ mkdir add && cd add
$ cat << EOF > Cargo.toml
[workspace]
members = [
"adder",
"add-one",
]
EOF
$ cargo new adder
Created binary (application) `adder` package
$ cargo new add-one --lib
Created library `add-one` package
$ tree
├── Cargo.lock
├── Cargo.toml
├── add-one
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── adder
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
$ echo 'add-one = { path = "../add-one" }' >> adder/Cargo.toml
Then we can use the add_one
function from add-one
crate in the
adder
crate:
// adder/src/main.rs
use add_one;
fn main() {
let num = 10;
println!(
"Hello, world! {} plus one is {}!",
num,
add_one::add_one(num)
);
}
Adding an external dependency
To add an external dependency on add-one
, just add to the
add-one/Cargo.toml
:
rand = "0.8.3"
Building at root level will fetch the rand
crate and the
top-level Cargo.lock
will contain information about the
dependency of add-one
on rand
.
However, it won’t be usable on adder
even though adder
has
dependency on add-one
, i.e. the dependency is not transitive.
In order to add rand
to adder
, you also need to add the
dependency on adder/Cargo.toml
.
Tests in workspaces
# at top level, it will run all crates tests and doc tests
cargo test
# run tests for a one particular crate in a worspace
cargo test -p add-one
Installing binaries from crate.io with cargo install
$ # command allows you to install and use binary crates locally
$ cargo install ripgrep
Updating crates.io index
Downloaded ripgrep v11.0.2
Downloaded 1 crate (243.3 KB) in 0.88s
Installing ripgrep v11.0.2
--snip--
Compiling ripgrep v11.0.2
Finished release [optimized + debuginfo] target(s) in 3m 10s
Installing ~/.cargo/bin/rg
Installed package `ripgrep v11.0.2` (executable `rg`)
Extending cargo with custom commands
Cargo is designed so you can extend it with new subcommands without having to modify Cargo. If a binary in your $PATH is named cargo-something, you can run it as if it was a Cargo subcommand by running cargo something. Custom commands like this are also listed when you run cargo —list. Being able to use cargo install to install extensions and then run them just like the built-in Cargo tools is a super convenient benefit of Cargo’s design!