# Rust Library Development Guidelines **Document Version:** v1 > **Note on Versioning:** > - This document version is independent — reused across projects > - **Project version** source of truth: `Cargo.toml` under `[package]` > - `CHANGELOG.md` uses project version from `Cargo.toml` ## Related Documents - **README.md** — Project overview, public API description, installation and usage examples - **AGENTS.md** — Rules for AI assistants - **PROJECT.md** — Project goals and current state - **CHANGELOG.md** — Version history --- ## 1. Code Style - **Rust edition:** 2021 - Format with **rustfmt** — run `cargo fmt` before every commit - Lint with **clippy** — run `cargo clippy -- -D warnings` before every commit - **snake_case** functions/variables/modules, **PascalCase** types/traits, **SCREAMING_SNAKE_CASE** constants --- ## 2. Cargo ```bash cargo add # Add runtime dependency cargo add --dev # Add dev dependency cargo remove # Remove dependency cargo test # Run all tests cargo doc --open # Build and open documentation cargo fmt # Format code cargo clippy -- -D warnings # Lint (treat warnings as errors) cargo build # Build to verify compilation cargo publish # Publish to crates.io ``` Never edit `Cargo.toml` dependency versions by hand — use `cargo add`. --- ## 2. Project Structure ``` project/ ├── src/ │ ├── lib.rs # Public API — re-export everything the caller needs │ ├── error.rs # All public error types │ └── / │ └── mod.rs ├── tests/ # Integration tests (test the public API only) ├── examples/ # Usage examples ├── Cargo.toml └── Cargo.lock # Do NOT commit — add to .gitignore ``` No `main.rs` — libraries have no entry point. --- ## 3. Public API - Everything intended for external use must be `pub` and re-exported from `lib.rs` - Use `pub(crate)` for internal items that cross module boundaries - Internal modules should be private (`mod foo;`) unless they are part of the public API - Public API must be stable across patch versions; breaking changes require a major version bump - Use `#[non_exhaustive]` on public enums and structs to allow adding fields without breaking changes ```rust // lib.rs — public surface pub use error::MyError; pub use client::Client; pub use types::{Config, Response}; ``` --- ## 4. Error Handling - Define all public error types in `src/error.rs` using **thiserror** - Export all errors from `lib.rs` - Never use `.unwrap()` — use `?` or explicit handling - `.expect("reason")` only when the invariant is guaranteed and documented ```rust // error.rs use thiserror::Error; #[derive(Debug, Error)] pub enum MyError { #[error("invalid input: {0}")] InvalidInput(String), #[error("IO error: {0}")] Io(#[from] std::io::Error), } ``` --- ## 5. Logging This is a library. Libraries must **never configure logging sinks** — that is the responsibility of the consuming application. Use **tracing** for instrumentation: ```toml [dependencies] tracing = "0.1" ``` ```rust use tracing::{debug, info, warn, error}; pub fn do_something() { debug!("internal detail"); info!("milestone reached"); } ``` Never call `tracing_subscriber::fmt().init()` or any sink setup inside library code. The consuming application configures the subscriber. #### Log levels | Level | When to use | |-------|-------------| | `debug` | Per-item detail: internal state, cache hits | | `info` | Significant milestones visible to the consuming application | | `warn` | Recoverable issues: fallback used, unexpected but non-fatal | | `error` | Failures the caller must know about | --- ## 6. Environment and Secrets Libraries do not read environment variables or `.env` files. Configuration is passed by the caller via arguments or constructor parameters. --- ## 7. Testing - Use Rust's built-in test framework — `#[test]` and `#[cfg(test)]` - Unit tests live in the same file as the code, in a `mod tests` block - Integration tests in `tests/` test only the public API (as a real consumer would) - Test naming: `test__` ```rust #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_valid_input() { assert_eq!(parse("42"), Ok(42)); } } ``` `Cargo.lock` is **not committed** for libraries — add it to `.gitignore`. Consumers pin their own dependency graph. --- ## 8. Documentation All public items must have doc comments. Use `cargo doc --open` to verify locally. ```rust /// Parses a value from a string. /// /// # Errors /// /// Returns [`MyError::InvalidInput`] if the string is not a valid value. /// /// # Examples /// /// ``` /// use my_crate::parse; /// assert_eq!(parse("42"), Ok(42)); /// ``` pub fn parse(s: &str) -> Result { ... } ``` `README.md` must contain installation instructions and usage examples for the public API. --- ## 9. Tooling | Tool | Purpose | |------|---------| | **rustfmt** | Code formatting | | **clippy** | Linting | | **cargo test** | Testing | | **cargo doc** | Documentation | Run before every commit: ```bash cargo fmt cargo clippy -- -D warnings cargo test ``` --- ## 10. Distribution Build and publish with Cargo: ```bash cargo package # Verify what will be published cargo publish # Publish to crates.io (requires login) ``` `Cargo.lock` is **not committed** — add to `.gitignore`. --- ## 11. Versioning - Follow **semantic versioning**: `MAJOR.MINOR.PATCH` - Version is defined in `Cargo.toml` under `[package]` - Always ask before bumping the version — never increment automatically - Update `CHANGELOG.md` before bumping the version - Breaking changes to the public API require a major version bump --- ## 12. Documentation and Task Management - Keep `PROJECT.md` and `CHANGELOG.md` up to date when making changes - `README.md` must contain installation instructions and usage examples for the public API ### Task notation ```rust // TODO: one-liner description of a task to be done // FIXME: one-liner description of a known bug to be fixed ```