More About Cargo and Crates.io
We've already covered the basics of Cargo in earlier chapters—how to create projects, build code, and run tests. Now we'll explore more advanced features that Cargo provides for optimizing your builds, sharing your code with the world, organizing large projects, and distributing binary tools.
Release Profiles
Cargo has a concept called profiles that control how your code is compiled in different contexts. A profile is a collection of settings that determine compilation behavior. Cargo ships with four built-in profiles: dev, release, test, and bench. Each profile has its own set of optimization settings.
The Dev Profile
When you run cargo build without the --release flag, Cargo uses the dev profile. This profile is optimized for iteration during development:
$ cargo build
Compiling my_oxide_app v0.1.0
Finished dev [unoptimized + debuginfo] target/debug/my_oxide_app
The dev profile prioritizes compilation speed over runtime performance. It includes debug information, which makes binaries larger and slower but allows you to debug them effectively with tools like gdb.
The Release Profile
When you're ready to deploy your code, use the --release flag:
$ cargo build --release
Compiling my_oxide_app v0.1.0
Finished release [optimized] target/release/my_oxide_app
The release profile applies aggressive optimizations:
- Optimizes for speed and size
- Removes debug information by default
- Takes longer to compile but produces much faster binaries
For production environments, always use --release builds. The performance difference can be dramatic—sometimes 10-100x faster than debug builds.
Customizing Profiles
You can customize profile settings in Cargo.toml by adding profile sections. For example, to add more optimizations to the release profile or add debug info to release builds:
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
debug = true
Common profile settings include:
| Setting | Default (dev) | Default (release) | Purpose |
|---|---|---|---|
opt-level | 0 | 3 | Optimization level (0-3, higher = more optimization) |
debug | true | false | Include debug symbols |
split-debuginfo | — | — | How to handle debug information |
debug-assertions | true | false | Include runtime assertions |
overflow-checks | true | false | Panic on integer overflow |
lto | false | false | Link-Time Optimization (slower compile, faster binary) |
panic | unwind | abort | Panic strategy |
incremental | true | false | Enable incremental compilation |
codegen-units | 256 | 16 | Parallel codegen units (lower = more optimization, slower compile) |
Creating Custom Profiles
You can create custom profiles beyond the built-in ones. For example, create a fast-compile profile that's optimized for quick iteration:
[profile.fast-compile]
inherits = "dev"
opt-level = 1
Build with your custom profile using cargo build --profile fast-compile.
Profile Settings in Practice
Here's a realistic release configuration that balances compile time with runtime performance:
[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 16
strip = true
[profile.dev]
opt-level = 0
debug-assertions = true
The strip = true setting removes symbols from the final binary, making it smaller. The lto = "thin" setting provides most of the benefits of link-time optimization with faster compile times than lto = true.
Publishing Crates to crates.io
Cargo and Rust's package registry crates.io make it straightforward to publish libraries for others to use. Publishing is free, and your code is permanently available.
Setting Up for Publication
Before publishing, ensure your Cargo.toml includes these fields:
[package]
name = "my_oxide_lib"
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
license = "MIT"
description = "A short description of what your library does"
repository = "https://github.com/yourusername/my_oxide_lib"
documentation = "https://docs.rs/my_oxide_lib"
Required Metadata
- name: Must be unique on crates.io and follow specific naming rules (alphanumeric, hyphens, underscores only)
- version: Must follow semantic versioning
- edition: The Rust edition your code targets
- authors: Your name and email
- license: At least one license identifier (MIT, Apache-2.0, GPL-3.0, etc.)
- description: A brief description displayed on crates.io
Documentation
Include a documentation comment at the top of your library's main file:
//! # My Oxide Library
//!
//! `myOxideLib` provides utilities for working with sequences of data.
//!
//! ## Examples
//!
//! ```
//! import myOxideLib.SearchOptions
//!
//! let options = SearchOptions.default()
//! ```
Documentation comments (starting with //!) describe the item they're attached to. Documentation tests (code blocks in doc comments) are automatically tested by cargo test.
Removing Items from Public API
Use the public import re-export pattern to control your public API:
//! My library documentation
public import self.kinds.PrimaryColor
public import self.utils.mix
public module kinds { ... }
module utils { ... }
This approach allows you to organize internal code while exposing a clean public API. Internal modules can remain private.
Publishing Your Crate
First, create an account on crates.io and get an API token from your account settings.
Log in to Cargo:
$ cargo login
Paste your token when prompted. This saves your credentials locally.
Then publish your crate:
$ cargo publish
Uploading my_oxide_lib v0.1.0 to registry index
Uploaded my_oxide_lib v0.1.0
Congratulations! Your crate is now available on crates.io. Others can use it by adding it to their Cargo.toml:
[dependencies]
my_oxide_lib = "0.1.0"
Semantic Versioning
Follow semantic versioning when publishing updates:
- MAJOR.MINOR.PATCH (e.g., 1.2.3)
- Increment MAJOR when making incompatible API changes
- Increment MINOR when adding new features in a backward-compatible way
- Increment PATCH for bug fixes
For pre-release versions, append a suffix like 0.1.0-alpha or 1.0.0-rc.1.
Deprecating Crate Versions
If you need to deprecate a version after publishing, use the yank command to prevent new downloads:
$ cargo yank --vers 1.0.1
This prevents new projects from depending on that version while allowing existing dependents to continue using it. You can undo a yank with --undo.
Workspaces
As projects grow, you often need to organize code into multiple related crates. Cargo workspaces allow you to manage multiple crates in a single repository with shared settings and dependencies.
Creating a Workspace
A workspace is defined by a Cargo.toml file in the root directory listing member crates:
[workspace]
members = [
"lib",
"app",
"cli",
]
[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
The [workspace.package] section defines shared metadata across all member crates.
Workspace Structure
my_workspace/
├── Cargo.toml
├── lib/
│ ├── Cargo.toml
│ └── src/
│ ├── main.ox
│ └── lib.ox
├── app/
│ ├── Cargo.toml
│ └── src/
│ └── main.ox
└── cli/
├── Cargo.toml
└── src/
└── main.ox
Each member crate has its own Cargo.toml file. The workspace root Cargo.toml coordinates the build.
Member Crate Configuration
Each member crate specifies its metadata:
lib/Cargo.toml:
[package]
name = "my_oxide_lib"
version.workspace = true
edition.workspace = true
authors.workspace = true
[lib]
name = "my_oxide_lib"
path = "src/lib.ox"
app/Cargo.toml:
[package]
name = "my_oxide_app"
version.workspace = true
edition.workspace = true
[dependencies]
my_oxide_lib = { path = "../lib" }
Using version.workspace = true references the version from the workspace manifest, avoiding duplication.
Building Workspaces
Build all members from the workspace root:
$ cargo build
Compiling my_oxide_lib v0.1.0
Compiling my_oxide_app v0.1.0
Finished dev [unoptimized + debuginfo]
Build specific members:
$ cargo build -p my_oxide_app
$ cargo run -p my_oxide_cli
Run tests for all members:
$ cargo test --workspace
Shared Dependencies
Dependencies can be shared across workspace members. When you specify a dependency in one member's Cargo.toml, Cargo downloads and compiles it once, then reuses it for other members.
This significantly speeds up builds and ensures consistency. If two crates depend on the same library, Cargo uses a single compiled version.
Workspaces vs. Monorepos
Workspaces are ideal for related crates that are versioned together. Each member crate:
- Has its own
src/directory and tests - Can be published independently to crates.io
- Can have different configurations
Use workspaces when:
- Multiple crates form a cohesive project
- You want to share code and dependencies
- Crates are released together
Use separate repositories when:
- Crates are independent projects
- Different teams maintain them
- They have different release cycles
Installing Binary Tools with cargo install
Cargo isn't just for building libraries and applications—it can also install binary tools globally on your system. This is how you install command-line utilities written in Rust or Oxide.
Installing Binaries
Install a binary crate from crates.io:
$ cargo install ripgrep
Installing ripgrep v13.0.0
Compiling regex v1.7.0
Compiling ripgrep v13.0.0
Finished `release` profile [optimized] target/release/ripgrep
Installed binary `rg` to ~/.cargo/bin/rg
The binary is installed to ~/.cargo/bin/, which should be in your PATH. If not, add it:
export PATH="$HOME/.cargo/bin:$PATH"
Installing from Local Paths
Install a binary from your local filesystem:
$ cargo install --path .
This is useful for distributing your own tools to team members or for development.
Installing Specific Versions
Install a particular version:
$ cargo install ripgrep --version 13.0.0
Or allow any version matching a pattern:
$ cargo install ripgrep --version ">=13"
Installing with Features
Some crates have optional features. Install with specific features enabled:
$ cargo install ripgrep --features gitignore
Listing Installed Binaries
View what binaries you've installed:
$ cargo install --list
my_oxide_tool v0.1.0 (path+file:///Users/yourname/project):
my_oxide_tool
ripgrep v13.0.0:
rg
Updating Installed Binaries
cargo install doesn't have a built-in update command, but you can reinstall to get the latest version:
$ cargo install ripgrep --force
The --force flag forces a reinstall even if the tool is already installed.
Making Your Crate Installable
To make your crate installable with cargo install, include a [[bin]] section in Cargo.toml:
[[bin]]
name = "my_oxide_tool"
path = "src/main.ox"
If your crate has exactly one binary in src/main.ox, this is implicit.
For crates with multiple binaries:
[[bin]]
name = "my_oxide_tool"
path = "src/bin/main.ox"
[[bin]]
name = "other_tool"
path = "src/bin/other.ox"
Users can then install your tool:
$ cargo install my_oxide_tool
Practical Example: A Complete Workspace
Let's create a real-world example: a workspace with a shared library and multiple applications using it.
Setting Up the Workspace
Create the workspace structure:
$ mkdir my_oxide_workspace
$ cd my_oxide_workspace
$ cargo new --oxide --lib shared
$ cargo new --oxide app
$ cargo new --oxide cli
Root Cargo.toml
[workspace]
members = ["shared", "app", "cli"]
[workspace.package]
version = "0.1.0"
edition = "2021"
authors = ["Your Name <you@example.com>"]
Shared Library (shared/src/lib.ox)
public struct Message {
public content: String,
public timestamp: UInt64,
}
public fn createMessage(content: String): Message {
Message {
content,
timestamp: getCurrentTimestamp(),
}
}
fn getCurrentTimestamp(): UInt64 {
// Implementation here
0
}
App Crate (app/Cargo.toml)
[package]
name = "my_oxide_app"
version.workspace = true
edition.workspace = true
[dependencies]
shared = { path = "../shared" }
Building and Running
$ cargo build --workspace
Compiling shared v0.1.0
Compiling my_oxide_app v0.1.0
Compiling my_oxide_cli v0.1.0
Finished dev [unoptimized + debuginfo]
$ cargo run -p my_oxide_app
Finished dev [unoptimized + debuginfo]
Running `target/debug/my_oxide_app`
Summary
We've explored Cargo's advanced features for professional development:
- Release Profiles let you customize compilation settings for different scenarios
- Publishing to crates.io makes your libraries available to the entire community
- Workspaces help you organize multiple related crates in a single project
- cargo install distributes binary tools for easy system-wide installation
These features make Cargo an exceptionally powerful tool for managing Oxide and Rust projects at scale. Whether you're building libraries for others, organizing complex multi-crate projects, or distributing command-line tools, Cargo provides the structure and automation to do it efficiently.
Oxide-Specific Notes
Remember that Oxide code (.ox files) works seamlessly in all of these scenarios:
- Profiles: Apply to Oxide code just like Rust code
- Publishing: Oxide libraries can be published to crates.io just like Rust libraries
- Workspaces: Mix Oxide and Rust crates freely in the same workspace
- cargo install: Works with Oxide binaries just as well as Rust binaries
When creating new projects in a workspace, use cargo new --oxide to create Oxide-specific projects with .ox files instead of .rs files.
In the next chapter, we'll dive deeper into Oxide's unique features and how they differ from Rust.