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.iobut 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.5is compatible with versions>= 1.3.5and< 2.0.00.3.5is compatible with versions>= 0.3.5and< 0.4.00.0.3only 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.lockyour 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+httpsandgit+sshare 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
unsafecode- 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.rsfile
- 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
bindgento 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:
opensslandopenssl-syszstdandzstd-sysrusqliteandlibsqlite3-sys