cargo publish
Goal of This Episode
Learn to publish your library to crates.io, making it available to Rust developers worldwide.
Concept
So far we’ve learned to organize code, write documentation, and use other people’s crates. This episode flips the direction — publishing a project of your own.
Account Setup
First, you need a crates.io account:
- Go to crates.io and log in with a GitHub account.
- On the account settings page, generate an API Token.
- In the terminal, run:
cargo login
After pressing enter, the terminal prompts you to paste the token — paste it, press enter again, done. The token is stored locally and used automatically for future publishes.
Preparing Cargo.toml
Before publishing, Cargo.toml needs some required metadata:
[package]
name = "my-awesome-lib"
version = "0.1.0"
edition = "2024"
description = "A wonderful math utility library"
license = "MIT"
repository = "https://github.com/yourname/my-awesome-lib"
readme = "README.md"
keywords = ["math", "utility"]
categories = ["mathematics"]
Per the official documentation, fill in before publishing:
license(orlicense-file): the open-source license (e.g.MIT,Apache-2.0,MIT OR Apache-2.0).description: a one-line summaryhomepage: the project homepage URLrepository: the source repository URLreadme: the README file’s path
Recommended but not required:
keywords: search keywords (up to 5)categories: categories (must match crates.io’s category list)
Pre-publish Checks
Before publishing, cargo package checks for problems:
cargo package
This simulates the packaging process, checking for missing required fields and other issues.
Publish!
Once everything’s ready:
cargo publish
Done! Your project is now on crates.io, and anyone can cargo add my-awesome-lib.
The Version Update Flow
After publishing, to release an update:
- Modify the code.
- Bump
versioninCargo.toml, following SemVer (semantic versioning). cargo publishagain.
SemVer’s rules:
- Before 1.0 (
0.x.y): the whole API is considered unstable; any release may break things. - After 1.0:
- Bug fixes:
1.0.0→1.0.1(patch). - New features (backward compatible):
1.0.1→1.1.0(minor). - Breaking changes:
1.1.0→2.0.0(major) — the first number changes.
- Bug fixes:
Why does SemVer fuss so much over “breaking changes”? Because once you’ve published, your public API (especially the pub things) is no longer just your own business — other people’s programs use your functions and depend on your type and method declarations. Your public API becomes a promise to your users: the surface they depend on isn’t yours to change on a whim.
The promise isn’t limited to pub things: documented behavior can be part of it too. Private implementation details remain yours to change as long as those promises still hold. The question most worth asking before publishing or updating is: “Do I really want to maintain this pub long-term?” The more you publish, the more you promise, and the less room remains for changing things without breaking someone. Keeping the unnecessary private (or pub(crate)) preserves your future freedom to change.
Note: published versions can’t be deleted or overwritten. If a version turns out badly broken, cargo yank marks it as discouraged — but those already using it are unaffected:
cargo yank --version 0.1.0
Best Done Before Publishing
- Write a good
README.md(shown on thecrate’s crates.io page). - Run
cargo testand confirm all tests pass. - Write doc comments with
///(last episode’s lesson). - Make sure there’s example code.
- Check the docs look right with
cargo doc --open.
Example Code
The complete structure of a small library ready for publishing:
my-math-lib/
├── Cargo.toml
├── README.md
└── src/
└── lib.rs
Cargo.toml:
[package]
name = "my-math-lib"
version = "0.1.0"
edition = "2024"
description = "Simple math utility functions"
license = "MIT"
homepage = "https://example.com/my-math-lib"
repository = "https://github.com/example/my-math-lib"
readme = "README.md"
keywords = ["math", "utility"]
categories = ["mathematics"]
src/lib.rs:
//! # My Math Lib
//!
//! Provides simple, handy math functions.
/// Computes the greatest common divisor.
///
/// # Examples
///
/// ```
/// use my_math_lib::gcd;
///
/// assert_eq!(gcd(12, 8), 4);
/// ```
pub fn gcd(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
/// Computes the least common multiple.
///
/// # Examples
///
/// ```
/// use my_math_lib::lcm;
///
/// assert_eq!(lcm(4, 6), 12);
/// ```
pub fn lcm(a: u64, b: u64) -> u64 {
if a == 0 || b == 0 {
return 0;
}
a / gcd(a, b) * b
}
/// Determines whether a number is prime.
///
/// # Examples
///
/// ```
/// use my_math_lib::is_prime;
///
/// assert!(is_prime(7));
/// assert!(!is_prime(4));
/// ```
pub fn is_prime(n: u64) -> bool {
if n < 2 {
return false;
}
let mut i: u64 = 2;
while i * i <= n {
if n % i == 0 {
return false;
}
i += 1;
}
true
}
fn main() {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gcd() {
assert_eq!(gcd(12, 8), 4);
assert_eq!(gcd(7, 3), 1);
assert_eq!(gcd(0, 5), 5);
}
#[test]
fn test_lcm() {
assert_eq!(lcm(4, 6), 12);
assert_eq!(lcm(0, 5), 0);
}
#[test]
fn test_is_prime() {
assert!(!is_prime(0));
assert!(!is_prime(1));
assert!(is_prime(2));
assert!(is_prime(17));
assert!(!is_prime(15));
}
}
The publishing command sequence:
cargo test # Confirm the tests pass
cargo doc --open # Check the docs
cargo package # Simulate packaging
cargo publish # Publish for real!
Recap
- Log into crates.io with GitHub, generate an API token, then configure with
cargo login. - Before publishing,
Cargo.tomlshould havelicense,description,homepage,repository,readme. cargo packagechecks for problems before publishing.cargo publishpublishes to crates.io for real.- Bump the
versionfield for updates, following SemVer (semantic versioning). - Your public API (especially the
pubthings) is a promise to your users; removing or incompatibly changing a public item is a breaking change (major). Backward-compatible additions belong in a minor release, but not every addition is backward compatible. Documented behavior can also be part of the promise; private implementation details may change as long as the promises still hold. - Published versions can’t be deleted;
cargo yankmerely marks them as discouraged. - Writing the README, doc comments, and tests before publishing is basic respect for your users.
Congratulations on finishing Chapter 7! 🎉 By this point, we’ve covered Rust’s major concepts — ownership, borrowing, generics, traits, lifetimes, closures, iterators, plus the module system and how to build and publish Cargo projects. You can now stand on your own. If there’s an idea in your head, now is a great time to build it!
Even so, Rust has many more distinctive and powerful features. The chapters ahead continue with important topics not yet covered, aiming to give you a more complete, well-rounded understanding of Rust.