Specifying Dependencies

Your crates can depend on other libraries from crates.io or otherregistries, git repositories, or subdirectories on your local file system.You can also temporarily override the location of a dependency — for example,to be able to test out a bug fix in the dependency that you are working onlocally. You can have different dependencies for different platforms, anddependencies that are only used during development. Let's take a look at howto do each of these.

Specifying dependencies from crates.io

Cargo is configured to look for dependencies on crates.io by default. Onlythe name and a version string are required in this case. In the cargoguide, we specified a dependency on the time crate:

  1. [dependencies]
  2. time = "0.1.12"

The string "0.1.12" is a semver version requirement. Since thisstring does not have any operators in it, it is interpreted the same way asif we had specified "^0.1.12", which is called a caret requirement.

Caret requirements

Caret requirements allow SemVer compatible updates to a specified version.An update is allowed if the new version number does not modify the left-mostnon-zero digit in the major, minor, patch grouping. In this case, if we rancargo update -p time, cargo should update us to version 0.1.13 if it is thelatest 0.1.z release, but would not update us to 0.2.0. If instead we hadspecified the version string as ^1.0, cargo should update to 1.1 if it isthe latest 1.y release, but not 2.0. The version 0.0.x is not consideredcompatible with any other version.

Here are some more examples of caret requirements and the versions that wouldbe allowed with them:

  1. ^1.2.3 := >=1.2.3 <2.0.0
  2. ^1.2 := >=1.2.0 <2.0.0
  3. ^1 := >=1.0.0 <2.0.0
  4. ^0.2.3 := >=0.2.3 <0.3.0
  5. ^0.2 := >=0.2.0 <0.3.0
  6. ^0.0.3 := >=0.0.3 <0.0.4
  7. ^0.0 := >=0.0.0 <0.1.0
  8. ^0 := >=0.0.0 <1.0.0

This compatibility convention is different from SemVer in the way it treatsversions before 1.0.0. While SemVer says there is no compatibility before1.0.0, Cargo considers 0.x.y to be compatible with 0.x.z, where y ≥ zand x > 0.

Tilde requirements

Tilde requirements specify a minimal version with some ability to update.If you specify a major, minor, and patch version or only a major and minorversion, only patch-level changes are allowed. If you only specify a majorversion, then minor- and patch-level changes are allowed.

~1.2.3 is an example of a tilde requirement.

  1. ~1.2.3 := >=1.2.3 <1.3.0
  2. ~1.2 := >=1.2.0 <1.3.0
  3. ~1 := >=1.0.0 <2.0.0

Wildcard requirements

Wildcard requirements allow for any version where the wildcard ispositioned.

, 1. and 1.2.* are examples of wildcard requirements.

  1. * := >=0.0.0
  2. 1.* := >=1.0.0 <2.0.0
  3. 1.2.* := >=1.2.0 <1.3.0

Comparison requirements

Comparison requirements allow manually specifying a version range or anexact version to depend on.

Here are some examples of comparison requirements:

  1. >= 1.2.0
  2. > 1
  3. < 2
  4. = 1.2.3

Multiple requirements

Multiple version requirements can also be separated with a comma, e.g., >= 1.2, < 1.5.

Specifying dependencies from other registries

To specify a dependency from a registry other than crates.io, first theregistry must be configured in a .cargo/config file. See the registriesdocumentation for more information. In the dependency, set the registry keyto the name of the registry to use.

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

Specifying dependencies from git repositories

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

  1. [dependencies]
  2. rand = { git = "https://github.com/rust-lang-nursery/rand" }

Cargo will fetch the git repository at this location then look for aCargo.toml for the requested crate anywhere inside the git repository(not necessarily at the root - for example, specifying a member crate nameof a workspace and setting git to the repository containing the workspace).

Since we haven’t specified any other information, Cargo assumes thatwe intend to use the latest commit on the master branch to build our package.You can combine the git key with the rev, tag, or branch keys tospecify something else. Here's an example of specifying that you want to usethe latest commit on a branch named next:

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

Specifying path dependencies

Over time, our hello_world package from the guide hasgrown significantly in size! It’s gotten to the point that we probably want tosplit out a separate crate for others to use. To do this Cargo supports pathdependencies which are typically sub-crates that live within one repository.Let’s start off by making a new crate inside of our hello_world package:

  1. # inside of hello_world/
  2. $ cargo new hello_utils

This will create a new folder hello_utils inside of which a Cargo.toml andsrc folder are ready to be configured. In order to tell Cargo about this, openup hello_world/Cargo.toml and add hello_utils to your dependencies:

  1. [dependencies]
  2. hello_utils = { path = "hello_utils" }

This tells Cargo that we depend on a crate called hello_utils which is foundin the hello_utils folder (relative to the Cargo.toml it’s written in).

And that’s it! The next cargo build will automatically build hello_utils andall of its own dependencies, and others can also start using the crate as well.However, crates that use dependencies specified with only a path are notpermitted on crates.io. If we wanted to publish our hello_world crate, wewould need to publish a version of hello_utils to crates.ioand specify its version in the dependencies line as well:

  1. [dependencies]
  2. hello_utils = { path = "hello_utils", version = "0.1.0" }

Overriding dependencies

There are a number of methods in Cargo to support overriding dependencies andotherwise controlling the dependency graph. These options are typically, though,only available at the workspace level and aren't propagated throughdependencies. In other words, "applications" have the ability to overridedependencies but "libraries" do not.

The desire to override a dependency or otherwise alter some dependencies canarise through a number of scenarios. Most of them, however, boil down to theability to work with a crate before it's been published to crates.io. Forexample:

  • A crate you're working on is also used in a much larger application you'reworking on, and you'd like to test a bug fix to the library inside of thelarger application.
  • An upstream crate you don't work on has a new feature or a bug fix on themaster branch of its git repository which you'd like to test out.
  • You're about to publish a new major version of your crate, but you'd like todo integration testing across an entire package to ensure the new majorversion works.
  • You've submitted a fix to an upstream crate for a bug you found, but you'dlike to immediately have your application start depending on the fixed versionof the crate to avoid blocking on the bug fix getting merged.These scenarios are currently all solved with the [patch] manifestsection. Historically some of these scenarios have been solvedwith the [replace] section, but we'll document the [patch]section here.

Testing a bugfix

Let's say you're working with the uuid crate but while you're working on ityou discover a bug. You are, however, quite enterprising so you decide to alsotry to fix the bug! Originally your manifest will look like:

  1. [package]
  2. name = "my-library"
  3. version = "0.1.0"
  4. authors = ["..."]
  5. [dependencies]
  6. uuid = "1.0"

First thing we'll do is to clone the uuid repositorylocally via:

  1. $ git clone https://github.com/rust-lang-nursery/uuid

Next we'll edit the manifest of my-library to contain:

  1. [patch.crates-io]
  2. uuid = { path = "../path/to/uuid" }

Here we declare that we're patching the source crates-io with a newdependency. This will effectively add the local checked out version of uuid tothe crates.io registry for our local package.

Next up we need to ensure that our lock file is updated to use this new versionof uuid so our package uses the locally checked out copy instead of one fromcrates.io. The way [patch] works is that it'll load the dependency at../path/to/uuid and then whenever crates.io is queried for versions of uuidit'll also return the local version.

This means that the version number of the local checkout is significant and willaffect whether the patch is used. Our manifest declared uuid = "1.0" whichmeans we'll only resolve to >= 1.0.0, < 2.0.0, and Cargo's greedy resolutionalgorithm also means that we'll resolve to the maximum version within thatrange. Typically this doesn't matter as the version of the git repository willalready be greater or match the maximum version published on crates.io, but it'simportant to keep this in mind!

In any case, typically all you need to do now is:

  1. $ cargo build
  2. Compiling uuid v1.0.0 (.../uuid)
  3. Compiling my-library v0.1.0 (.../my-library)
  4. Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs

And that's it! You're now building with the local version of uuid (note thepath in parentheses in the build output). If you don't see the local path version gettingbuilt then you may need to run cargo update -p uuid —precise $version where$version is the version of the locally checked out copy of uuid.

Once you've fixed the bug you originally found the next thing you'll want to dois to likely submit that as a pull request to the uuid crate itself. Onceyou've done this then you can also update the [patch] section. The listinginside of [patch] is just like the [dependencies] section, so once your pullrequest is merged you could change your path dependency to:

  1. [patch.crates-io]
  2. uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

Working with an unpublished minor version

Let's now shift gears a bit from bug fixes to adding features. While working onmy-library you discover that a whole new feature is needed in the uuidcrate. You've implemented this feature, tested it locally above with [patch],and submitted a pull request. Let's go over how you continue to use and test itbefore it's actually published.

Let's also say that the current version of uuid on crates.io is 1.0.0, butsince then the master branch of the git repository has updated to 1.0.1. Thisbranch includes your new feature you submitted previously. To use thisrepository we'll edit our Cargo.toml to look like

  1. [package]
  2. name = "my-library"
  3. version = "0.1.0"
  4. authors = ["..."]
  5. [dependencies]
  6. uuid = "1.0.1"
  7. [patch.crates-io]
  8. uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

Note that our local dependency on uuid has been updated to 1.0.1 as it'swhat we'll actually require once the crate is published. This version doesn'texist on crates.io, though, so we provide it with the [patch] section of themanifest.

Now when our library is built it'll fetch uuid from the git repository andresolve to 1.0.1 inside the repository instead of trying to download a versionfrom crates.io. Once 1.0.1 is published on crates.io the [patch] section canbe deleted.

It's also worth noting that [patch] applies transitively. Let's say you usemy-library in a larger package, such as:

  1. [package]
  2. name = "my-binary"
  3. version = "0.1.0"
  4. authors = ["..."]
  5. [dependencies]
  6. my-library = { git = 'https://example.com/git/my-library' }
  7. uuid = "1.0"
  8. [patch.crates-io]
  9. uuid = { git = 'https://github.com/rust-lang-nursery/uuid' }

Remember that [patch] is applicable transitively but can only be defined atthe top level so we consumers of my-library have to repeat the [patch] sectionif necessary. Here, though, the new uuid crate applies to both our dependency onuuid and the my-library -> uuid dependency. The uuid crate will be resolved toone version for this entire crate graph, 1.0.1, and it'll be pulled from the gitrepository.

Overriding repository URL

In case the dependency you want to override isn't loaded from crates.io, you'll have to change a bit how you use [patch]:

  1. [patch."https://github.com/your/repository"]
  2. my-library = { path = "../my-library/path" }

And that's it!

Prepublishing a breaking change

As a final scenario, let's take a look at working with a new major version of acrate, typically accompanied with breaking changes. Sticking with our previouscrates, this means that we're going to be creating version 2.0.0 of the uuidcrate. After we've submitted all changes upstream we can update our manifest formy-library to look like:

  1. [dependencies]
  2. uuid = "2.0"
  3. [patch.crates-io]
  4. uuid = { git = "https://github.com/rust-lang-nursery/uuid", branch = "2.0.0" }

And that's it! Like with the previous example the 2.0.0 version doesn't actuallyexist on crates.io but we can still put it in through a git dependency throughthe usage of the [patch] section. As a thought exercise let's take anotherlook at the my-binary manifest from above again as well:

  1. [package]
  2. name = "my-binary"
  3. version = "0.1.0"
  4. authors = ["..."]
  5. [dependencies]
  6. my-library = { git = 'https://example.com/git/my-library' }
  7. uuid = "1.0"
  8. [patch.crates-io]
  9. uuid = { git = 'https://github.com/rust-lang-nursery/uuid', branch = '2.0.0' }

Note that this will actually resolve to two versions of the uuid crate. Themy-binary crate will continue to use the 1.x.y series of the uuid crate butthe my-library crate will use the 2.0.0 version of uuid. This will allow youto gradually roll out breaking changes to a crate through a dependency graphwithout being force to update everything all at once.

Overriding with local dependencies

Sometimes you're only temporarily working on a crate and you don't want to haveto modify Cargo.toml like with the [patch] section above. For this usecase Cargo offers a much more limited version of overrides called pathoverrides.

Path overrides are specified through .cargo/config instead of Cargo.toml,and you can find more documentation about this configuration.Inside of .cargo/config you'll specify a key called paths:

  1. paths = ["/path/to/uuid"]

This array should be filled with directories that contain a Cargo.toml. Inthis instance, we’re just adding uuid, so it will be the only one that’soverridden. This path can be either absolute or relative to the directory thatcontains the .cargo folder.

Path overrides are more restricted than the [patch] section, however, inthat they cannot change the structure of the dependency graph. When apath replacement is used then the previous set of dependenciesmust all match exactly to the new Cargo.toml specification. For example thismeans that path overrides cannot be used to test out adding a dependency to acrate, instead [patch] must be used in that situation. As a result usage of apath override is typically isolated to quick bug fixes rather than largerchanges.

Note: using a local configuration to override paths will only work for cratesthat have been published to crates.io. You cannot use this feature to tellCargo how to find local unpublished crates.

Platform specific dependencies

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

  1. [target.'cfg(windows)'.dependencies]
  2. winhttp = "0.4.0"
  3. [target.'cfg(unix)'.dependencies]
  4. openssl = "1.0.1"
  5. [target.'cfg(target_arch = "x86")'.dependencies]
  6. native = { path = "native/i686" }
  7. [target.'cfg(target_arch = "x86_64")'.dependencies]
  8. native = { path = "native/x86_64" }

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

If you want to know which cfg targets are available on your platform, runrustc —print=cfg from the command line. If you want to know which cfgtargets 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 = "my_crate")'.dependencies] to adddependencies based on optional crate features.Use the [features] sectioninstead.

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

  1. [target.x86_64-pc-windows-gnu.dependencies]
  2. winhttp = "0.4.0"
  3. [target.i686-unknown-linux-gnu.dependencies]
  4. openssl = "1.0.1"

If you’re using a custom target specification, quote the full path and filename:

  1. [target."x86_64/windows.json".dependencies]
  2. winhttp = "0.4.0"
  3. [target."i686/linux.json".dependencies]
  4. openssl = "1.0.1"
  5. native = { path = "native/i686" }
  6. [target."x86_64/linux.json".dependencies]
  7. openssl = "1.0.1"
  8. native = { path = "native/x86_64" }

Development dependencies

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

  1. [dev-dependencies]
  2. tempdir = "0.3"

Dev-dependencies are not used when compilinga package for building, but are used for compiling tests, examples, andbenchmarks.

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

You can also have target-specific development dependencies by usingdev-dependencies in the target section header instead of dependencies. Forexample:

  1. [target.'cfg(unix)'.dev-dependencies]
  2. 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 themanifest:

  1. [build-dependencies]
  2. cc = "1.0.3"

The build script does not have access to the dependencies listedin the dependencies or dev-dependencies section. Builddependencies will likewise not be available to the package itselfunless listed under the dependencies section as well. A packageitself and its build script are built separately, so theirdependencies need not coincide. Cargo is kept simpler and cleaner byusing independent dependencies for independent purposes.

Choosing features

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

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

More information about features can be found in themanifest documentation.

Renaming dependencies in Cargo.toml

When writing a [dependencies] section in Cargo.toml the key you write for adependency typically matches up to the name of the crate you import from in thecode. For some projects, though, you may wish to reference the crate with adifferent name in the code regardless of how it's published on crates.io. Forexample 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] sectionof which package should be depended on:
  1. [package]
  2. name = "mypackage"
  3. version = "0.0.1"
  4. [dependencies]
  5. foo = "0.1"
  6. bar = { git = "https://github.com/example/project", package = "foo" }
  7. baz = { version = "0.1", registry = "custom", package = "foo" }

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

  1. # #![allow(unused_variables)]
  2. #fn main() {
  3. extern crate foo; // crates.io
  4. extern crate bar; // git repository
  5. extern crate baz; // registry `custom`
  6. #}

All three of these crates have the package name of foo in their ownCargo.toml, so we're explicitly using the package key to inform Cargo thatwe want the foo package even though we're calling it something else locally.The package key, if not specified, defaults to the name of the dependencybeing requested.

Note that if you have an optional dependency like:

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

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

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

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