Rust

Semver Compliance with cargo-semver-checks

Automatically detect breaking API changes before publishing Rust crates.

Semver Compliance with cargo-semver-checks

When you maintain public crates, accidentally shipping a breaking change in a patch or minor release erodes trust. cargo-semver-checks lints your API diff against the last published version and catches violations before cargo publish.

Install

cargo install cargo-semver-checks

Basic Usage

Run from your crate directory:

cargo semver-checks

This compares your local code against the latest version on crates.io. If you've removed a public function, changed a signature, or removed a trait impl, it flags it.

Check Against a Specific Baseline

# Compare against a specific published version
cargo semver-checks --baseline-version 0.5.0

# Compare against a git rev
cargo semver-checks --baseline-rev v0.5.0

What It Catches

cargo-semver-checks runs lints — each one detects a specific kind of breaking change:

CategoryExamples
RemovalsPublic function/struct/enum/trait removed
Signature changesParameter added, return type changed
Trait changesRequired method added, impl removed
Type changesStruct field made private, enum variant removed
GenericsType parameter added/removed, bound tightened
Re-exportsPublic re-export removed

What It Doesn't Catch

  • Behavioral/semantic changes (function returns different values)
  • Performance regressions
  • Changes behind feature flags (partial support)

Integration with Pre-Publish Workflow

Add it to your publish checklist, right after tests and before cargo publish --dry-run:

#!/bin/bash
set -e

cargo test
cargo clippy -- -D warnings
cargo semver-checks          # ← catches breaking changes
cargo publish --dry-run

In a Justfile

[doc("Check semver compliance against crates.io")]
semver-check:
    cargo semver-checks

[doc("Full pre-publish validation")]
pre-publish: test clippy semver-check
    cargo publish --dry-run

Real-World Example

Suppose you rename a function in rfconversions:

// Before (v0.7.2)
pub fn db_to_linear(db: f64) -> f64 { ... }

// After (local)
pub fn decibels_to_linear(db: f64) -> f64 { ... }

Running cargo semver-checks:

--- failure: function_missing ---
Description: A publicly-visible function has been removed or renamed.
   Changed: rfconversions::db_to_linear
   Removed in: (local)
   Previously in: 0.7.2

This tells you: bump the major version, or add a deprecated alias.

Deprecation-Friendly Migration

Instead of breaking, keep the old name as a deprecated alias:

pub fn decibels_to_linear(db: f64) -> f64 {
    10f64.powf(db / 10.0)
}

#[deprecated(since = "0.8.0", note = "renamed to decibels_to_linear")]
pub fn db_to_linear(db: f64) -> f64 {
    decibels_to_linear(db)
}

Now cargo semver-checks passes — no breaking change. Downstream users get a compiler warning guiding them to migrate.

Multi-Crate Workspaces

For workspace roots with multiple crates:

# Check all crates in workspace
cargo semver-checks --workspace

# Check a specific crate
cargo semver-checks -p gainlineup

CI Integration

Add to GitHub Actions:

- name: Check semver
  uses: obi1kenobi/cargo-semver-checks-action@v2

This blocks PRs that introduce unintentional breaking changes.

Tips

  • Run it before bumping the version in Cargo.toml — it compares against the published version
  • Use --baseline-version when you want to check against an older release
  • Pair with #[deprecated] for graceful migrations
  • Over 80 lints and growing — stays current with Rust editions
  • First run downloads and caches rustdoc JSON for the baseline; subsequent runs are fast

Further Reading