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:

SettingDefault (dev)Default (release)Purpose
opt-level03Optimization level (0-3, higher = more optimization)
debugtruefalseInclude debug symbols
split-debuginfoHow to handle debug information
debug-assertionstruefalseInclude runtime assertions
overflow-checkstruefalsePanic on integer overflow
ltofalsefalseLink-Time Optimization (slower compile, faster binary)
panicunwindabortPanic strategy
incrementaltruefalseEnable incremental compilation
codegen-units25616Parallel 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.