Dependency Management with Cargo
Cargo.toml - A manifest file
[package]
name = "tcp-mailbox"
version = "0.1.0"
[dependencies]
async-std = "1" # would also choose 1.5
clap = "2.2" # would also choose 2.3
Cargo.lock - A lock file
- contains a list of all project dependencies, de-facto versions and hashes of downloaded dependencies
- when a version is yanked from
Crates.io
but you have the correct hash for it in a lock file Cargo will still let you download it and use it- still gives you warning about that version being problematic
- should be committed to your repository for applications
Dependency resolution
- uses "Zero-aware" SemVer for versioning
1.3.5
is compatible with versions>= 1.3.5
and< 2.0.0
0.3.5
is compatible with versions>= 0.3.5
and< 0.4.0
0.0.3
only allows0.0.3
- allows version-incompatible transitive dependencies
- except C/C++ dependencies
- combines dependencies with compatible requirements as much as possible
- allows path, git, and custom registry dependencies
How a dependency version is selected
- for every requirement Cargo selects acceptable version intervals
[1.1.0; 1.6.0)
,[1.3.5, 2.0.0)
,[2.0.0; 3.0.0)
- Cargo checks for interval intersections to reduce the number of unique intervals
[1.3.5; 1.6.0)
,[2.0.0; 3.0.0)
- for every unique interval it selects the most recent available version
=1.5.18
,=2.7.11
- selected versions and corresponding package hashes are written into
Cargo.lock
Dependency resolution: Example
└── my-app May install:
├── A = "1"
│ ├── X = "1" A = "1.0.17"
│ └── Y = "1.3" => B = "1.5.0"
└── B = "1" X = "2.0.3"
├── X = "2" X = "1.2.14"
└── Y = "1.5" Y = "1.8.5"
Where do dependencies come from?
- Crates.io
- Private registries (open-source, self-hosted, or hosted)
- Git and Path dependencies
- dependencies can be vendored
Notes:
- private registries
Shipyard and Kellnr will also generate API docs for you
Crates.io
- default package registry
- 100k crates and counting
- every Rust Beta release is tested against all of them every week
- packages aren't deleted, but yanked
- if you have a correct hash for a yanked version in your
Cargo.lock
your build won't break (you still get a warning)
- if you have a correct hash for a yanked version in your
Docs.rs
- complete API documentation for the whole Rust ecosystem
- automatically publishes API documentation for every version of every crate on Crates.io
- documentation for old versions stays up, too. Easy to switch between versions.
- links across crates just work
Other kinds of dependencies
- git dependencies
- both
git+https
andgit+ssh
are allowed - can specify branch, tag, commit hash
- when downloaded by Cargo exact commit hash used is written into
Cargo.lock
- both
- path dependencies
- both relative and absolute paths are allowed
- common in workspaces
C Libraries as dependencies
- Rust can call functions from C libraries using
unsafe
code- integrate with operating system APIs, frameworks, SDKs, etc.
- talk to custom hardware
- reuse existing code (SQLite, OpenSSL, libgit2, etc.)
- building a crate that relies on C libraries often requires customization
- done using
build.rs
file
- done using
build.rs
file
- compiled and executed before the rest of the package
- can manipulate files, execute external programs, etc.
- download / install custom SDKs
- call
cc
,cmake
, etc. to build C++ dependencies - execute
bindgen
to generate Rust bindings to C libraries
- output can be used to set Cargo options dynamically
println!("cargo:rustc-link-lib=gizmo"); println!("cargo:rustc-link-search=native={}/gizmo/", library_path);
-sys
crates
- often Rust libraries that integrate with C are split into a pair of crates:
library-name-sys
- thin wrapper around C functions
- often all code is autogenerated by
bindgen
library-name
- depends on
library-name-sys
- exposes convenient and idiomatic Rust API to users
- depends on
- examples:
openssl
andopenssl-sys
zstd
andzstd-sys
rusqlite
andlibsqlite3-sys