knowledge/technology/applications/development/cargo.md
2024-08-30 10:38:15 +02:00

77 KiB
Raw Blame History

obj repo rev
application https://github.com/rust-lang/cargo 2024-08-30

cargo

Cargo is the official package manager for the Rust programming language. It serves as a build system, package manager, and dependency manager for Rust projects. Cargo makes it easy to manage, build, and distribute Rust projects, handling tasks such as compiling code, managing dependencies, and running tests.

cargo add

Add dependencies to a Cargo.toml manifest file

Options

Option Description
--no-default-features Disable the default features
--default-features Re-enable the default features
-F, --features <FEATURES> Space or comma separated list of features to activate
--optional Mark the dependency as optional. The package name will be exposed as feature of your crate.
--no-optional Mark the dependency as required. The package will be removed from your features.
--rename <NAME> Rename the dependency
--dry-run Don't actually write the manifest
-q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
--manifest-path <PATH> Path to Cargo.toml
--path <PATH> Filesystem path to local crate to add
--git <URI> Git repository location
--branch <BRANCH> Git branch to download the crate from
--tag <TAG> Git tag to download the crate from
--rev <REV> Git reference to download the crate from
--registry <NAME> Package registry for this dependency
--dev Add as development dependency. Dev-dependencies are not used when compiling a package for building, but are used for compiling tests, examples, and benchmarks. These dependencies are not propagated to other packages which depend on this package.
--build Add as build dependency. Build-dependencies are the only dependencies available for use by build scripts (build.rs files).
--target <TARGET> Add as dependency to the given target platform

cargo build

Compile a local package and all of its dependencies

Options

Option Description
--lib Build only this package's library
--bins Build all binaries
--bin [<NAME>] Build only the specified binary
--examples Build all examples
--example [<NAME>] Build only the specified example
--tests Build all tests
--test [<NAME>] Build only the specified test target
--all-targets Build all targets
-F, --features <FEATURES> Space or comma separated list of features to activate
--all-features Activate all available features
--no-default-features Do not activate the default feature
-r, --release Build artifacts in release mode, with optimizations
-j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.

cargo check

Check a local package and all of its dependencies for errors

cargo clean

Remove artifacts that cargo has generated in the past
Usage: cargo clean

cargo clippy

Checks a package to catch common mistakes and improve your Rust code.

Options

Option Description
--no-deps Run Clippy only on the given crate, without linting the dependencies
--fix Automatically apply lint suggestions. This flag implies --no-deps and --all-targets
--explain LINT Print the documentation for a given lint

To allow or deny a lint from the command line you can use cargo clippy -- with:

Option Description
-W --warn OPT Set lint warnings
-A --allow OPT Set lint allowed
-D --deny OPT Set lint denied
-F --forbid OPT Set lint forbidden

cargo doc

Build a package's documentation

Options

Option Description
--open Opens the docs in a browser after the operation
--no-deps Don't build documentation for dependencies
--document-private-items Document private items

cargo fetch

Fetch dependencies of a package from the network

cargo fmt

Formats all bin and lib files of the current crate using rustfmt.
Usage: cargo fmt [--check]

cargo init

Create a new cargo package in an existing directory

Options

Option Description
--bin Use a binary (application) template [default]
--lib Use a library template
--edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018, 2021]
--name <NAME> Set the resulting package name, defaults to the directory name

cargo install

Install a Rust binary. Default location is $HOME/.cargo/bin

Options

Option Description
--version <VERSION> Specify a version to install
--index <INDEX> Registry index to install from
--registry <REGISTRY> Registry to use
--git <URL> Git URL to install the specified crate from
--branch <BRANCH> Branch to use when installing from git
--tag <TAG> Tag to use when installing from git
--rev <SHA> Specific commit to use when installing from git
--path <PATH> Filesystem path to local crate to install
--root <DIR> Directory to install packages into
-f, --force Force overwriting existing crates or binaries
--no-track Do not save tracking information
--list list all installed packages and their versions
-q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
--bin [<NAME>] Install only the specified binary
--bins Install all binaries
--example [<NAME>] Install only the specified example
--examples Install all examples

cargo login

Log in to a registry.
Usage: cargo login --registry registry <token>

cargo logout

Remove an API token from the registry locally.
Usage: cargo logout --registry reg

cargo new

Create a new cargo package

Options

Option Description
--bin Use a binary (application) template [default]
--lib Use a library template
--edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018, 2021]
--name <NAME> Set the resulting package name, defaults to the directory name

cargo package

Assemble the local package into a distributable tarball

Options

Option Description
-l, --list Print files included in a package without making one
--no-verify Don't verify the contents by building them
--no-metadata Ignore warnings about a lack of human-usable metadata
--allow-dirty Allow dirty working directories to be packaged
-q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)

cargo publish

Upload a package to the registry

Options

Option Description
--dry-run Perform all checks without uploading
--index <INDEX> Registry index URL to upload the package to
--registry <REGISTRY> Registry to publish to
--token <TOKEN> Token to use when uploading
--no-verify Don't verify the contents by building them
--allow-dirty Allow dirty working directories to be packaged
-q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)

cargo metadata

Print a JSON representation of a Cargo.toml manifest.
Usage: cargo metadata --no-deps

cargo remove

Remove dependencies from a Cargo.toml manifest file

Options

Option Description
--dry-run Don't actually write the manifest
--dev Remove as development dependency
--build Remove as build dependency
--target <TARGET> Remove as dependency from the given target platform

cargo run

Run a binary or example of the local package

Options

Option Description
--bin <NAME> Name of the bin target to run
--example <NAME> Name of the example target to run
-F, --features <FEATURES> Space or comma separated list of features to activate
--all-features Activate all available features
--no-default-features Do not activate the default feature
-j, --jobs <N> Number of parallel jobs, defaults to # of CPUs
-r, --release Build artifacts in release mode, with optimizations

Search packages in registry.
Usage: cargo search [--registry registry] [--limit limit] package

cargo test

Execute all unit and integration tests and build examples of a local package

cargo tree

Display a tree visualization of a dependency graph

cargo uninstall

Remove a Rust binary

cargo update

Update dependencies as recorded in the local lock file
Usage: cargo update [--dry-run]

cargo flamegraph

You can generate flamegraphs for your rust project with this project.

Install with cargo install flamegraph.

If you use mold or ldd, you have to add this:
lld:

[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]

mold:

[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-Clink-arg=-fuse-ld=/usr/local/bin/mold", "-Clink-arg=-Wl,--no-rosegment"]

Usage: cargo flamegraph [OPTIONS] [-- <TRAILING_ARGUMENTS>...]

Options

Option Description
--dev Build with the dev profile
--profile <PROFILE> Build with the specified profile
-p, --package <PACKAGE> package with the binary to run
-b, --bin <BIN> Binary to run
--example <EXAMPLE> Example to run
--test <TEST> Test binary to run (currently profiles the test harness and all tests in the binary)
--unit-test [<UNIT_TEST>] Crate target to unit test, may be omitted if crate only has one target (currently profiles the test harness and all tests in the binary; test selection can be passed as trailing arguments after -- as separator)
--bench <BENCH> Benchmark to run
--manifest-path <MANIFEST_PATH> Path to Cargo.toml
-f, --features <FEATURES> Build features to enable
--no-default-features Disable default features
-r, --release No-op. For compatibility with cargo run --release
-o, --output <OUTPUT> Output file [default: flamegraph.svg]
--open Open the output .svg file with default program
--root Run with root privileges (using sudo)
-F, --freq <FREQUENCY> Sampling frequency in Hz [default: 997]
-c, --cmd <CUSTOM_CMD> Custom command for invoking perf/dtrace
--deterministic Colors are selected such that the color of a function does not change between runs
-i, --inverted Plot the flame graph up-side-down
--reverse Generate stack-reversed flame graph
--notes <STRING> Set embedded notes in SVG
--min-width <FLOAT> Omit functions smaller than pixels [default: 0.01]
--image-width <IMAGE_WIDTH> Image width in pixels
--palette <PALETTE> Color palette [possible values: hot, mem, io, red, green, blue, aqua, yellow, purple, orange, wakeup, java, perl, js, rust]
--skip-after <FUNCTION> Cut off stack frames below ; may be repeated
--flamechart Produce a flame chart (sort by time, do not merge stacks)
--ignore-status Ignores perf's exit code
--no-inline Disable inlining for perf script because of performance issues
--post-process <POST_PROCESS> Run a command to process the folded stacks, taking the input from stdin and outputting to stdout

Manifest

The Cargo.toml file for each package is called its manifest. It is written in the TOML format. It contains metadata that is needed to compile the package.

The [package] section

The first section in a Cargo.toml is [package].

[package]
name = "hello_world" # the name of the package
version = "0.1.0"    # the current version, obeying semver
authors = ["Alice <a@example.com>", "Bob <b@example.com>"]

The name field

The package name is an identifier used to refer to the package. It is used when listed as a dependency in another package, and as the default name of inferred lib and bin targets.

The version field

Cargo bakes in the concept of Semantic Versioning, so make sure you follow some basic rules:

  • Before you reach 1.0.0, anything goes, but if you make breaking changes, increment the minor version. In Rust, breaking changes include adding fields to structs or variants to enums.
  • After 1.0.0, only make breaking changes when you increment the major version. Dont break the build.
  • After 1.0.0, dont add any new public API (no new pub anything) in patch-level versions. Always increment the minor version if you add any new pub structs, traits, fields, types, functions, methods or anything else.
  • Use version numbers with three numeric parts such as 1.0.0 rather than 1.0.

This field is optional and defaults to 0.0.0. The field is required for publishing packages.

The authors field

The optional authors field lists in an array the people or organizations that are considered the “authors” of the package. The exact meaning is open to interpretation — it may list the original or primary authors, current maintainers, or owners of the package. An optional email address may be included within angled brackets at the end of each author entry.

[package]
# ...
authors = ["Graydon Hoare", "Fnu Lnu <no-reply@rust-lang.org>"]

This field is only surfaced in package metadata and in the $CARGO_PKG_AUTHORS environment variable within build.rs. It is not displayed in the crates.io user interface.

Warning

: Package manifests cannot be changed once published, so this field cannot be changed or removed in already-published versions of a package.

The edition field

The edition key is an optional key that affects which Rust Edition your package is compiled with. Setting the edition key in [package] will affect all targets/crates in the package, including test suites, benchmarks, binaries, examples, etc.

[package]
# ...
edition = '2021'

Most manifests have the edition field filled in automatically by cargo new with the latest stable edition. By default cargo new creates a manifest with the 2021 edition currently.

If the edition field is not present in Cargo.toml, then the 2015 edition is assumed for backwards compatibility. Note that all manifests created with cargo new will not use this historical fallback because they will have edition explicitly specified to a newer value.

The description field

The description is a short blurb about the package. crates.io will display this with your package. This should be plain text (not Markdown).

[package]
# ...
description = "A short description of my package"

Note

: crates.io requires the description to be set.

The documentation field

The documentation field specifies a URL to a website hosting the crates documentation. If no URL is specified in the manifest file, crates.io will automatically link your crate to the corresponding docs.rs page when the documentation has been built and is available.

[package]
# ...
documentation = "https://docs.rs/bitflags"

The readme field

The readme field should be the path to a file in the package root (relative to this Cargo.toml) that contains general information about the package. This file will be transferred to the registry when you publish. crates.io will interpret it as Markdown and render it on the crates page.

[package]
# ...
readme = "README.md"

If no value is specified for this field, and a file named README.md, README.txt or README exists in the package root, then the name of that file will be used. You can suppress this behavior by setting this field to false. If the field is set to true, a default value of README.md will be assumed.

The homepage field

The homepage field should be a URL to a site that is the home page for your package.

[package]
# ...
homepage = "https://serde.rs"

A value should only be set for homepage if there is a dedicated website for the crate other than the source repository or API documentation. Do not make homepage redundant with either the documentation or repository values.

The repository field

The repository field should be a URL to the source repository for your package.

[package]
# ...
repository = "https://github.com/rust-lang/cargo"

The license and license-file fields

The license field contains the name of the software license that the package is released under. The license-file field contains the path to a file containing the text of the license (relative to this Cargo.toml).

crates.io interprets the license field as an SPDX 2.3 license expression. The name must be a known license from the SPDX license list 3.20. See the SPDX site for more information.

SPDX license expressions support AND and OR operators to combine multiple licenses.

[package]
# ...
license = "MIT OR Apache-2.0"

Using OR indicates the user may choose either license. Using AND indicates the user must comply with both licenses simultaneously. The WITH operator indicates a license with a special exception. Some examples:

  • MIT OR Apache-2.0
  • LGPL-2.1-only AND MIT AND BSD-2-Clause
  • GPL-2.0-or-later WITH Bison-exception-2.2

If a package is using a nonstandard license, then the license-file field may be specified in lieu of the license field.

[package]
# ...
license-file = "LICENSE.txt"

Note

: crates.io requires either license or license-file to be set.

The keywords field

The keywords field is an array of strings that describe this package. This can help when searching for the package on a registry, and you may choose any words that would help someone find this crate.

[package]
# ...
keywords = ["gamedev", "graphics"]

Note

: crates.io allows a maximum of 5 keywords. Each keyword must be ASCII text, have at most 20 characters, start with an alphanumeric character, and only contain letters, numbers, _, - or +.

The categories field

The categories field is an array of strings of the categories this package belongs to.

categories = ["command-line-utilities", "development-tools::cargo-plugins"]

Note

: crates.io has a maximum of 5 categories. Each category should match one of the strings available at https://crates.io/category_slugs, and must match exactly.

The workspace field

The workspace field can be used to configure the workspace that this package will be a member of. If not specified this will be inferred as the first Cargo.toml with [workspace] upwards in the filesystem. Setting this is useful if the member is not inside a subdirectory of the workspace root.

[package]
# ...
workspace = "path/to/workspace/root"

This field cannot be specified if the manifest already has a [workspace] table defined. That is, a crate cannot both be a root crate in a workspace (contain [workspace]) and also be a member crate of another workspace (contain package.workspace).

The build field

The build field specifies a file in the package root which is a build script for building native code.

[package]
# ...
build = "build.rs"

The default is build.rs, which loads the script from a file named build.rs in the root of the package. Use build = "custom_build_name.rs" to specify a path to a different file or build = false to disable automatic detection of the build script.

The links field specifies the name of a native library that is being linked to.
For example, a crate that links a native library called “git2” (e.g. libgit2.a on Linux) may specify:

[package]
# ...
links = "git2"

The exclude and include fields

The exclude and include fields can be used to explicitly specify which files are included when packaging a project to be published, and certain kinds of change tracking. The patterns specified in the exclude field identify a set of files that are not included, and the patterns in include specify files that are explicitly included. You may run cargo package --list to verify which files will be included in the package.

[package]
# ...
exclude = ["/ci", "images/", ".*"]

[package]
# ...
include = ["/src", "COPYRIGHT", "/examples", "!/examples/big_example"]

The publish field

The publish field can be used to control which registries names the package may be published to:

[package]
# ...
publish = ["some-registry-name"]

To prevent a package from being published to a registry (like crates.io) by mistake, for instance to keep a package private in a company, you can omit the version field. If youd like to be more explicit, you can disable publishing:

[package]
# ...
publish = false

If publish array contains a single registry, cargo publish command will use it when --registry flag is not specified.

The metadata table

Cargo by default will warn about unused keys in Cargo.toml to assist in detecting typos and such. The package.metadata table, however, is completely ignored by Cargo and will not be warned about. This section can be used for tools which would like to store package configuration in Cargo.toml. For example:

[package]
name = "..."
# ...

# Metadata used when generating an Android APK, for example.
[package.metadata.android]
package-name = "my-awesome-android-app"
assets = "path/to/static"

Dependencies

Your crates can depend on other libraries from crates.io or other registries, git repositories, or subdirectories on your local file system.

Cargo is configured to look for dependencies on crates.io by default. Only the name and a version string are required in this case:

[dependencies]
time = "0.1.12"

The string 0.1.12 is a version requirement. Although it looks like a specific version of the time crate, it actually specifies a range of versions and allows SemVer compatible updates. An update is allowed if the new version number does not modify the left-most non-zero number in the major, minor, patch grouping. In this case, if we ran cargo update time, cargo should update us to version 0.1.13 if it is the latest 0.1.z release, but would not update us to 0.2.0. If instead we had specified the version string as 1.0, cargo should update to 1.1 if it is the latest 1.y release, but not 2.0. The version 0.0.x is not considered compatible with any other version.

Specifying dependencies from other registries

To specify a dependency from a registry other than crates.io set the registry key to the name of the registry to use:

[dependencies]
some-crate = { version = "1.0", registry = "my-registry" }

where my-registry is the registry name configured in .cargo/config.toml file.

Note

: crates.io does not allow packages to be published with dependencies on code published outside of crates.io.

Specifying dependencies from git repositories

To depend on a library located in a git repository, the minimum information you need to specify is the location of the repository with the git key:

[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git" }

Cargo fetches the git repository at that location and traverses the file tree to find Cargo.toml file for the requested crate anywhere inside the git repository. For example, regex-lite and regex-syntax are members of rust-lang/regex repo and can be referred to by the repos root URL (https://github.com/rust-lang/regex.git) regardless of where in the file tree they reside.

regex-lite   = { git = "https://github.com/rust-lang/regex.git" }
regex-syntax = { git = "https://github.com/rust-lang/regex.git" }
Choice of commit

Cargo assumes that we intend to use the latest commit on the default branch to build our package if we only specify the repo URL, as in the examples above.

You can combine the git key with the rev, tag, or branch keys to be more specific about which commit to use. Heres an example of using the latest commit on a branch named next:

[dependencies]
regex = { git = "https://github.com/rust-lang/regex.git", branch = "next" }

Anything that is not a branch or a tag falls under rev key. This can be a commit hash like rev = "4c59b707", or a named reference exposed by the remote repository such as rev = "refs/pull/493/head".

What references are available for the rev key varies by where the repo is hosted.

More git dependency examples:

# .git suffix can be omitted if the host accepts such URLs - both examples work the same
regex = { git = "https://github.com/rust-lang/regex" }
regex = { git = "https://github.com/rust-lang/regex.git" }

# a commit with a particular tag
regex = { git = "https://github.com/rust-lang/regex.git", tag = "1.10.3" }

# a commit by its SHA1 hash
regex = { git = "https://github.com/rust-lang/regex.git", rev = "0c0990399270277832fbb5b91a1fa118e6f63dba" }

# HEAD commit of PR 493
regex = { git = "https://github.com/rust-lang/regex.git", rev = "refs/pull/493/head" }

# INVALID EXAMPLES

# specifying the commit after # ignores the commit ID and generates a warning
regex = { git = "https://github.com/rust-lang/regex.git#4c59b70" }

# git and path cannot be used at the same time
regex = { git = "https://github.com/rust-lang/regex.git#4c59b70", path = "../regex" }

Cargo locks the commits of git dependencies in Cargo.lock file at the time of their addition and checks for updates only when you run cargo update command.

The role of the version key

The version key always implies that the package is available in a registry, regardless of the presence of git or path keys.

The version key does not affect which commit is used when Cargo retrieves the git dependency, but Cargo checks the version information in the dependencys Cargo.toml file against the version key and raises an error if the check fails.

In this example, Cargo retrieves the HEAD commit of the branch called next from Git and checks if the crates version is compatible with version = "1.10.3":

[dependencies]
regex = { version = "1.10.3", git = "https://github.com/rust-lang/regex.git", branch = "next" }

version, git, and path keys are considered separate locations for resolving the dependency.

Specifying path dependencies

Cargo supports path dependencies which are typically sub-crates that live within one repository.

[dependencies]
hello_utils = { path = "hello_utils" }

This tells Cargo that we depend on a crate called hello_utils which is found in the hello_utils folder, relative to the Cargo.toml file its written in.

The next cargo build will automatically build hello_utils and all of its dependencies.

The local paths must point to the exact folder with the dependencys Cargo.toml. Unlike with git dependencies, Cargo does not traverse local paths. For example, if regex-lite and regex-syntax are members of a locally cloned rust-lang/regex repo, they have to be referred to by the full path:

# git key accepts the repo root URL and Cargo traverses the tree to find the crate
[dependencies]
regex-lite   = { git = "https://github.com/rust-lang/regex.git" }
regex-syntax = { git = "https://github.com/rust-lang/regex.git" }

# path key requires the member name to be included in the local path
[dependencies]
regex-lite   = { path = "../regex/regex-lite" }
regex-syntax = { path = "../regex/regex-syntax" }

Local paths in published crates

Crates that use dependencies specified with only a path are not permitted on crates.io.

Multiple locations

It is possible to specify both a registry version and a git or path location. The git or path dependency will be used locally (in which case the version is checked against the local copy), and when published to a registry like crates.io, it will use the registry version. Other combinations are not allowed. Examples:

[dependencies]
# Uses `my-bitflags` when used locally, and uses
# version 1.0 from crates.io when published.
bitflags = { path = "my-bitflags", version = "1.0" }

# Uses the given git repo when used locally, and uses
# version 1.0 from crates.io when published.
smallvec = { git = "https://github.com/servo/rust-smallvec.git", version = "1.0" }

Platform specific dependencies

Platform-specific dependencies take the same format, but are listed under a target section. Normally Rust-like #[cfg] syntax will be used to define these sections:

[target.'cfg(windows)'.dependencies]
winhttp = "0.4.0"

[target.'cfg(unix)'.dependencies]
openssl = "1.0.1"

[target.'cfg(target_arch = "x86")'.dependencies]
native-i686 = { path = "native/i686" }

[target.'cfg(target_arch = "x86_64")'.dependencies]
native-x86_64 = { path = "native/x86_64" }

Like with Rust, the syntax here supports the not, any, and all operators to combine various cfg name/value pairs.

If you want to know which cfg targets are available on your platform, run rustc --print=cfg from the command line. If you want to know which cfg targets are available for another platform, such as 64-bit Windows, run rustc --print=cfg --target=x86_64-pc-windows-msvc.

Unlike in your Rust source code, you cannot use [target.'cfg(feature = "fancy-feature")'.dependencies] to add dependencies based on optional features. Use the [features] section instead:

[dependencies]
foo = { version = "1.0", optional = true }
bar = { version = "1.0", optional = true }

[features]
fancy-feature = ["foo", "bar"]

The same applies to cfg(debug_assertions), cfg(test) and cfg(proc_macro). These values will not work as expected and will always have the default value returned by rustc --print=cfg. There is currently no way to add dependencies based on these configuration values.

In addition to #[cfg] syntax, Cargo also supports listing out the full target the dependencies would apply to:

[target.x86_64-pc-windows-gnu.dependencies]
winhttp = "0.4.0"

[target.i686-unknown-linux-gnu.dependencies]
openssl = "1.0.1"

Custom target specifications

If youre using a custom target specification (such as --target foo/bar.json), use the base filename without the .json extension:

[target.bar.dependencies]
winhttp = "0.4.0"

[target.my-special-i686-platform.dependencies]
openssl = "1.0.1"
native = { path = "native/i686" }

Development dependencies

You can add a [dev-dependencies] section to your Cargo.toml whose format is equivalent to [dependencies]:

[dev-dependencies]
tempdir = "0.3"

Dev-dependencies are not used when compiling a package for building, but are used for compiling tests, examples, and benchmarks.

These dependencies are not propagated to other packages which depend on this package.

You can also have target-specific development dependencies by using dev-dependencies in the target section header instead of dependencies. For example:

[target.'cfg(unix)'.dev-dependencies]
mio = "0.0.1"

Build dependencies

You can depend on other Cargo-based crates for use in your build scripts. Dependencies are declared through the build-dependencies section of the manifest:

[build-dependencies]
cc = "1.0.3"

You can also have target-specific build dependencies by using build-dependencies in the target section header instead of dependencies. For example:

[target.'cfg(unix)'.build-dependencies]
cc = "1.0.3"

In this case, the dependency will only be built when the host platform matches the specified target.

The build script does not have access to the dependencies listed in the dependencies or dev-dependencies section. Build dependencies will likewise not be available to the package itself unless listed under the dependencies section as well. A package itself and its build script are built separately, so their dependencies need not coincide. Cargo is kept simpler and cleaner by using independent dependencies for independent purposes.

Choosing features

If a package you depend on offers conditional features, you can specify which to use:

[dependencies.awesome]
version = "1.3.5"
default-features = false # do not include the default features, and optionally
                         # cherry-pick individual features
features = ["secure-password", "civet"]

Renaming dependencies in Cargo.toml

When writing a [dependencies] section in Cargo.toml the key you write for a dependency typically matches up to the name of the crate you import from in the code. For some projects, though, you may wish to reference the crate with a different name in the code regardless of how its published on crates.io. For example you may wish to:

  • Avoid the need to use foo as bar in Rust source.
  • Depend on multiple versions of a crate.
  • Depend on crates with the same name from different registries.

To support this Cargo supports a package key in the [dependencies] section of which package should be depended on:

[package]
name = "mypackage"
version = "0.0.1"

[dependencies]
foo = "0.1"
bar = { git = "https://github.com/example/project.git", package = "foo" }
baz = { version = "0.1", registry = "custom", package = "foo" }

In this example, three crates are now available in your Rust code:

extern crate foo; // crates.io
extern crate bar; // git repository
extern crate baz; // registry `custom`

All three of these crates have the package name of foo in their own Cargo.toml, so were explicitly using the package key to inform Cargo that we want the foo package even though were calling it something else locally. The package key, if not specified, defaults to the name of the dependency being requested.

Note that if you have an optional dependency like:

[dependencies]
bar = { version = "0.1", package = 'foo', optional = true }

youre depending on the crate foo from crates.io, but your crate has a bar feature instead of a foo feature. That is, names of features take after the name of the dependency, not the package name, when renamed.

Enabling transitive dependencies works similarly, for example we could add the following to the above manifest:

[features]
log-debug = ['bar/log-debug'] # using 'foo/log-debug' would be an error!

The [lints] section

Override the default level of lints from different tools by assigning them to a new level in a table, for example:

[lints.rust]
unsafe_code = "forbid"

This is short-hand for:

[lints.rust]
unsafe_code = { level = "forbid", priority = 0 }

level corresponds to the lint levels in rustc:

  • forbid
  • deny
  • warn
  • allow

priority is a signed integer that controls which lints or lint groups override other lint groups. lower (particularly negative) numbers have lower priority, being overridden by higher numbers, and show up first on the command-line to tools like rustc

To know which table under [lints] a particular lint belongs under, it is the part before :: in the lint name. If there isnt a ::, then the tool is rust. For example a warning about unsafe_code would be lints.rust.unsafe_code but a lint about clippy::enum_glob_use would be lints.clippy.enum_glob_use.

For example:

[lints.rust]
unsafe_code = "forbid"

[lints.clippy]
enum_glob_use = "deny"

Cargo Targets

Cargo packages consist of targets which correspond to source files which can be compiled into a crate. Packages can have library, binary, example, test, and benchmark targets. The list of targets can be configured in the Cargo.toml manifest, often inferred automatically by the directory layout of the source files.

Library

The library target defines a “library” that can be used and linked by other libraries and executables. The filename defaults to src/lib.rs, and the name of the library defaults to the name of the package, with any dashes replaced with underscores. A package can have only one library. The settings for the library can be customized in the [lib] table in Cargo.toml.

# Example of customizing the library in Cargo.toml.
[lib]
crate-type = ["cdylib"]
bench = false

Binaries

Binary targets are executable programs that can be run after being compiled. The default binary filename is src/main.rs, which defaults to the name of the package. Additional binaries are stored in the src/bin/ directory. The settings for each binary can be customized in the [[bin]] tables in Cargo.toml.

Binaries can use the public API of the packages library. They are also linked with the [dependencies] defined in Cargo.toml.

You can run individual binaries with the cargo run command with the --bin <bin-name> option. cargo install can be used to copy the executable to a common location.

# Example of customizing binaries in Cargo.toml.
[[bin]]
name = "cool-tool"
test = false
bench = false

[[bin]]
name = "frobnicator"
required-features = ["frobnicate"]

Examples

Files located under the examples directory are example uses of the functionality provided by the library. When compiled, they are placed in the target/debug/examples directory.

Examples can use the public API of the packages library. They are also linked with the [dependencies] and [dev-dependencies] defined in Cargo.toml.

By default, examples are executable binaries (with a main() function). You can specify the crate-type field to make an example be compiled as a library:

[[example]]
name = "foo"
crate-type = ["staticlib"]

You can run individual executable examples with the cargo run command with the --example <example-name> option. Library examples can be built with cargo build with the --example <example-name> option. cargo install with the --example <example-name> option can be used to copy executable binaries to a common location. Examples are compiled by cargo test by default to protect them from bit-rotting. Set the test field to true if you have #[test] functions in the example that you want to run with cargo test.

Tests

There are two styles of tests within a Cargo project:

  • Unit tests which are functions marked with the #[test] attribute located within your library or binaries (or any target enabled with the test field). These tests have access to private APIs located within the target they are defined in.
  • Integration tests which is a separate executable binary, also containing #[test] functions, which is linked with the projects library and has access to its public API.

Tests are run with the cargo test command. By default, Cargo and rustc use the libtest harness which is responsible for collecting functions annotated with the #[test] attribute and executing them in parallel, reporting the success and failure of each test. See the harness field if you want to use a different harness or test strategy.

Note

: There is another special style of test in Cargo: documentation tests. They are handled by rustdoc and have a slightly different execution model.

Integration tests

Files located under the tests directory are integration tests. When you run cargo test, Cargo will compile each of these files as a separate crate, and execute them.

Integration tests can use the public API of the packages library. They are also linked with the [dependencies] and [dev-dependencies] defined in Cargo.toml.

If you want to share code among multiple integration tests, you can place it in a separate module such as tests/common/mod.rs and then put mod common; in each test to import it.

Each integration test results in a separate executable binary, and cargo test will run them serially. In some cases this can be inefficient, as it can take longer to compile, and may not make full use of multiple CPUs when running the tests. If you have a lot of integration tests, you may want to consider creating a single integration test, and split the tests into multiple modules. The libtest harness will automatically find all of the #[test] annotated functions and run them in parallel. You can pass module names to cargo test to only run the tests within that module.

Benchmarks

Benchmarks provide a way to test the performance of your code using the cargo bench command. They follow the same structure as tests, with each benchmark function annotated with the #[bench] attribute. Similarly to tests:

  • Benchmarks are placed in the benches directory.
  • Benchmark functions defined in libraries and binaries have access to the private API within the target they are defined in. Benchmarks in the benches directory may use the public API.
  • The bench field can be used to define which targets are benchmarked by default.
  • The harness field can be used to disable the built-in harness.

Note

: The #[bench] attribute is currently unstable and only available on the nightly channel. There are some packages available on crates.io that may help with running benchmarks on the stable channel, such as Criterion.

Configuring a target

All of the [lib], [[bin]], [[example]], [[test]], and [[bench]] sections in Cargo.toml support similar configuration for specifying how a target should be built. The double-bracket sections like [[bin]] are array-of-table of TOML, which means you can write more than one [[bin]] section to make several executables in your crate. You can only specify one library, so [lib] is a normal TOML table.

The following is an overview of the TOML settings for each target.

[lib]
name = "foo"           # The name of the target.
path = "src/lib.rs"    # The source file of the target.
test = true            # Is tested by default.
doctest = true         # Documentation examples are tested by default.
bench = true           # Is benchmarked by default.
doc = true             # Is documented by default.
plugin = false         # Used as a compiler plugin (deprecated).
proc-macro = false     # Set to `true` for a proc-macro library.
harness = true         # Use libtest harness.
edition = "2015"       # The edition of the target.
crate-type = ["lib"]   # The crate types to generate.
required-features = [] # Features required to build this target (N/A for lib).

Features

Cargo “features” provide a mechanism to express conditional compilation and optional dependencies. A package defines a set of named features in the [features] table of Cargo.toml, and each feature can either be enabled or disabled. Features for the package being built can be enabled on the command-line with flags such as --features. Features for dependencies can be enabled in the dependency declaration in Cargo.toml.

The [features] section

Features are defined in the [features] table in Cargo.toml. Each feature specifies an array of other features or optional dependencies that it enables. The following examples illustrate how features could be used for a 2D image processing library where support for different image formats can be optionally included:

[features]
# Defines a feature named `webp` that does not enable any other features.
webp = []

With this feature defined, cfg expressions can be used to conditionally include code to support the requested feature at compile time. For example, inside lib.rs of the package could include this:

// This conditionally includes a module which implements WEBP support.
#[cfg(feature = "webp")]
pub mod webp;

Cargo sets features in the package using the rustc --cfg flag, and code can test for their presence with the cfg attribute or the cfg macro.

Features can list other features to enable. For example, the ICO image format can contain BMP and PNG images, so when it is enabled, it should make sure those other features are enabled, too:

[features]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []

The default feature

By default, all features are disabled unless explicitly enabled. This can be changed by specifying the default feature:

[features]
default = ["ico", "webp"]
bmp = []
png = []
ico = ["bmp", "png"]
webp = []

When the package is built, the default feature is enabled which in turn enables the listed features. This behavior can be changed by:

  • The --no-default-features command-line flag disables the default features of the package.
  • The default-features = false option can be specified in a dependency declaration.

Note

: Be careful about choosing the default feature set. The default features are a convenience that make it easier to use a package without forcing the user to carefully select which features to enable for common use, but there are some drawbacks. Dependencies automatically enable default features unless default-features = false is specified. This can make it difficult to ensure that the default features are not enabled, especially for a dependency that appears multiple times in the dependency graph. Every package must ensure that default-features = false is specified to avoid enabling them.

Optional dependencies

Dependencies can be marked “optional”, which means they will not be compiled by default. For example, lets say that our 2D image processing library uses an external package to handle GIF images. This can be expressed like this:

[dependencies]
gif = { version = "0.11.1", optional = true }

By default, this optional dependency implicitly defines a feature that looks like this:

[features]
gif = ["dep:gif"]

This means that this dependency will only be included if the gif feature is enabled. The same cfg(feature = "gif") syntax can be used in the code, and the dependency can be enabled just like any feature such as --features gif.

In some cases, you may not want to expose a feature that has the same name as the optional dependency. For example, perhaps the optional dependency is an internal detail, or you want to group multiple optional dependencies together, or you just want to use a better name. If you specify the optional dependency with the dep: prefix anywhere in the [features] table, that disables the implicit feature.

For example, lets say in order to support the AVIF image format, our library needs two other dependencies to be enabled.

[dependencies]
ravif = { version = "0.6.3", optional = true }
rgb = { version = "0.8.25", optional = true }

[features]
avif = ["dep:ravif", "dep:rgb"]

In this example, the avif feature will enable the two listed dependencies. This also avoids creating the implicit ravif and rgb features, since we dont want users to enable those individually as they are internal details to our crate.

Note

: Another way to optionally include a dependency is to use platform-specific dependencies. Instead of using features, these are conditional based on the target platform.

Dependency features

Features of dependencies can be enabled within the dependency declaration. The features key indicates which features to enable.

[dependencies]
# Enables the `derive` feature of serde.
serde = { version = "1.0.118", features = ["derive"] }

The default features can be disabled using default-features = false:

[dependencies]
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }

Note

: This may not ensure the default features are disabled. If another dependency includes flate2 without specifying default-features = false, then the default features will be enabled.

Features of dependencies can also be enabled in the [features] table. The syntax is "package-name/feature-name". For example:

[dependencies]
jpeg-decoder = { version = "0.1.20", default-features = false }

[features]
# Enables parallel processing support by enabling the "rayon" feature of jpeg-decoder.
parallel = ["jpeg-decoder/rayon"]

The "package-name/feature-name" syntax will also enable package-name if it is an optional dependency. Often this is not what you want. You can add a ? as in "package-name?/feature-name" which will only enable the given feature if something else enables the optional dependency.

For example, lets say we have added some serialization support to our library, and it requires enabling a corresponding feature in some optional dependencies. That can be done like this:

[dependencies]
serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }

[features]
serde = ["dep:serde", "rgb?/serde"]

In this example, enabling the serde feature will enable the serde dependency. It will also enable the serde feature for the rgb dependency, but only if something else has enabled the rgb dependency.

Command-line feature options

The following command-line flags can be used to control which features are enabled:

  • --features FEATURES: Enables the listed features. Multiple features may be separated with commas or spaces. If using spaces, be sure to use quotes around all the features if running Cargo from a shell (such as --features "foo bar"). If building multiple packages in a workspace, the package-name/feature-name syntax can be used to specify features for specific workspace members.
  • --all-features: Activates all features of all packages selected on the command-line.
  • --no-default-features: Does not activate the default feature of the selected packages.

Mutually exclusive features

There are rare cases where features may be mutually incompatible with one another. This should be avoided if at all possible, because it requires coordinating all uses of the package in the dependency graph to cooperate to avoid enabling them together. If it is not possible, consider adding a compile error to detect this scenario. For example:

#[cfg(all(feature = "foo", feature = "bar"))]
compile_error!("feature \"foo\" and feature \"bar\" cannot be enabled at the same time");

Build scripts

Build scripts can detect which features are enabled on the package by inspecting the CARGO_FEATURE_<name> environment variable, where <name> is the feature name converted to uppercase and - converted to _.

Profiles

Profiles provide a way to alter the compiler settings, influencing things like optimizations and debugging symbols.

Cargo has 4 built-in profiles: dev, release, test, and bench. The profile is automatically chosen based on which command is being run if a profile is not specified on the command-line. In addition to the built-in profiles, custom user-defined profiles can also be specified.

Profile settings can be changed in Cargo.toml with the [profile] table. Within each named profile, individual settings can be changed with key/value pairs like this:

[profile.dev]
opt-level = 1               # Use slightly better optimizations.
overflow-checks = false     # Disable integer overflow checks.

Cargo only looks at the profile settings in the Cargo.toml manifest at the root of the workspace. Profile settings defined in dependencies will be ignored.

Additionally, profiles can be overridden from a config definition. Specifying a profile in a config file or environment variable will override the settings from Cargo.toml.

Profile Settings

The following is a list of settings that can be controlled in a profile.

opt-level

The opt-level setting controls the -C opt-level flag which controls the level of optimization. Higher optimization levels may produce faster runtime code at the expense of longer compiler times. Higher levels may also change and rearrange the compiled code which may make it harder to use with a debugger.

The valid options are:

  • 0: no optimizations
  • 1: basic optimizations
  • 2: some optimizations
  • 3: all optimizations
  • s: optimize for binary size
  • z: optimize for binary size, but also turn off loop vectorization.

It is recommended to experiment with different levels to find the right balance for your project. There may be surprising results, such as level 3 being slower than 2, or the "s" and "z" levels not being necessarily smaller. You may also want to reevaluate your settings over time as newer versions of rustc change optimization behavior.

debug

The debug setting controls the -C debuginfo flag which controls the amount of debug information included in the compiled binary.

The valid options are:

  • 0, false, or none: no debug info at all, default for release
  • line-directives-only: line info directives only. For the nvptx* targets this enables profiling. For other use cases, line-tables-only is the better, more compatible choice.
  • line-tables-only: line tables only. Generates the minimal amount of debug info for backtraces with filename/line number info, but not anything else, i.e. no variable or function parameter info.
  • 1 or limited: debug info without type or variable-level information. Generates more detailed module-level info than line-tables-only.
  • 2, true, or full: full debug info, default for dev
split-debuginfo

The split-debuginfo setting controls the -C split-debuginfo flag which controls whether debug information, if generated, is either placed in the executable itself or adjacent to it.

strip

The strip option controls the -C strip flag, which directs rustc to strip either symbols or debuginfo from a binary. This can be enabled like so:

[package]
# ...

[profile.release]
strip = "debuginfo"

Possible string values of strip are none, debuginfo, and symbols. The default is none.

debug-assertions

The debug-assertions setting controls the -C debug-assertions flag which turns cfg(debug_assertions) conditional compilation on or off. Debug assertions are intended to include runtime validation which is only available in debug/development builds. These may be things that are too expensive or otherwise undesirable in a release build. Debug assertions enables the debug_assert! macro in the standard library.

overflow-checks

The overflow-checks setting controls the -C overflow-checks flag which controls the behavior of runtime integer overflow. When overflow-checks are enabled, a panic will occur on overflow.

lto

The lto setting controls rustcs -C lto, -C linker-plugin-lto, and -C embed-bitcode options, which control LLVMs link time optimizations. LTO can produce better optimized code, using whole-program analysis, at the cost of longer linking time.

The valid options are:

  • false: Performs “thin local LTO” which performs “thin” LTO on the local crate only across its codegen units. No LTO is performed if codegen units is 1 or opt-level is 0.
  • true or fat: Performs “fat” LTO which attempts to perform optimizations across all crates within the dependency graph.
  • thin: Performs “thin” LTO. This is similar to “fat”, but takes substantially less time to run while still achieving performance gains similar to “fat”.
  • off: Disables LTO.
panic

The panic setting controls the -C panic flag which controls which panic strategy to use.

The valid options are:

  • unwind: Unwind the stack upon panic.
  • abort: Terminate the process upon panic.
incremental

The incremental setting controls the -C incremental flag which controls whether or not incremental compilation is enabled. Incremental compilation causes rustc to save additional information to disk which will be reused when recompiling the crate, improving re-compile times. The additional information is stored in the target directory.

codegen-units

The codegen-units setting controls the -C codegen-units flag which controls how many “code generation units” a crate will be split into. More code generation units allows more of a crate to be processed in parallel possibly reducing compile time, but may produce slower code.

This option takes an integer greater than 0.

The default is 256 for incremental builds, and 16 for non-incremental builds.

rpath

The rpath setting controls the -C rpath flag which controls whether or not rpath is enabled.

Default profiles

dev

The dev profile is used for normal development and debugging. It is the default for build commands like cargo build, and is used for cargo install --debug.

The default settings for the dev profile are:

[profile.dev]
opt-level = 0
debug = true
split-debuginfo = '...'  # Platform-specific.
strip = "none"
debug-assertions = true
overflow-checks = true
lto = false
panic = 'unwind'
incremental = true
codegen-units = 256
rpath = false
release

The release profile is intended for optimized artifacts used for releases and in production. This profile is used when the --release flag is used, and is the default for cargo install.

The default settings for the release profile are:

[profile.release]
opt-level = 3
debug = false
split-debuginfo = '...'  # Platform-specific.
strip = "none"
debug-assertions = false
overflow-checks = false
lto = false
panic = 'unwind'
incremental = false
codegen-units = 16
rpath = false
test

The test profile is the default profile used by cargo test. The test profile inherits the settings from the dev profile.

bench

The bench profile is the default profile used by cargo bench. The bench profile inherits the settings from the release profile.

Custom profiles

In addition to the built-in profiles, additional custom profiles can be defined. These may be useful for setting up multiple workflows and build modes. When defining a custom profile, you must specify the inherits key to specify which profile the custom profile inherits settings from when the setting is not specified.

For example, lets say you want to compare a normal release build with a release build with LTO optimizations, you can specify something like the following in Cargo.toml:

[profile.release-lto]
inherits = "release"
lto = true

The --profile flag can then be used to choose this custom profile:

cargo build --profile release-lto

The output for each profile will be placed in a directory of the same name as the profile in the target directory. As in the example above, the output would go into the target/release-lto directory.

Build Scripts

Some packages need to compile third-party non-Rust code, for example C libraries. Other packages need to link to C libraries which can either be located on the system or possibly need to be built from source. Others still need facilities for functionality such as code generation before building (think parser generators).

Cargo does not aim to replace other tools that are well-optimized for these tasks, but it does integrate with them with custom build scripts. Placing a file named build.rs in the root of a package will cause Cargo to compile that script and execute it just before building the package.

// Example custom build script.
fn main() {
    // Tell Cargo that if the given file changes, to rerun this build script.
    println!("cargo:rerun-if-changed=src/hello.c");
    // Use the `cc` crate to build a C file and statically link it.
    cc::Build::new()
        .file("src/hello.c")
        .compile("hello");
}

Environment Variables

When the build script is run, there are a number of inputs to the build script, all passed in the form of environment variables.

Cargo exposes these environment variables to your crate when it is compiled. Note that this applies for running binaries with cargo run and cargo test as well. To get the value of any of these variables in a Rust program, do this:

let version = env!("CARGO_PKG_VERSION");

Exposed environment variables:

  • CARGO — Path to the cargo binary performing the build.
  • CARGO_MANIFEST_DIR — The directory containing the manifest of your package.
  • CARGO_PKG_VERSION — The full version of your package.
  • CARGO_PKG_VERSION_MAJOR — The major version of your package.
  • CARGO_PKG_VERSION_MINOR — The minor version of your package.
  • CARGO_PKG_VERSION_PATCH — The patch version of your package.
  • CARGO_PKG_VERSION_PRE — The pre-release version of your package.
  • CARGO_PKG_AUTHORS — Colon separated list of authors from the manifest of your package.
  • CARGO_PKG_NAME — The name of your package.
  • CARGO_PKG_DESCRIPTION — The description from the manifest of your package.
  • CARGO_PKG_HOMEPAGE — The home page from the manifest of your package.
  • CARGO_PKG_REPOSITORY — The repository from the manifest of your package.
  • CARGO_PKG_LICENSE — The license from the manifest of your package.
  • CARGO_PKG_LICENSE_FILE — The license file from the manifest of your package.
  • CARGO_PKG_RUST_VERSION — The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version.
  • CARGO_PKG_README — Path to the README file of your package.
  • CARGO_CRATE_NAME — The name of the crate that is currently being compiled. It is the name of the Cargo target with - converted to _, such as the name of the library, binary, example, integration test, or benchmark.
  • CARGO_BIN_NAME — The name of the binary that is currently being compiled. Only set for binaries or binary examples. This name does not include any file extension, such as .exe.
  • OUT_DIR — If the package has a build script, this is set to the folder where the build script should place its output.
  • CARGO_BIN_EXE_<name> — The absolute path to a binary targets executable. This is only set when building an integration test or benchmark. This may be used with the env macro to find the executable to run for testing purposes. The <name> is the name of the binary target, exactly as-is. For example, CARGO_BIN_EXE_my-program for a binary named my-program. Binaries are automatically built when the test is built, unless the binary has required features that are not enabled.
  • CARGO_PRIMARY_PACKAGE — This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests).
  • CARGO_TARGET_TMPDIR — Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesnt manage its content in any way, this is the responsibility of the test code.