Skip to main content

rust-workspace

Design Rust project topology — single-crate packages, project-sized multi-crate workspaces (small root-flat, hybrid/domain-grouped, contained crates/, nested, or root-package layouts), workspace-level configuration, dependency DAGs, and crate-boundary decisions. Use when users ask how to split a project into crates, choose paths from the resulting project scale instead of copying a source-language module tree, configure a workspace, avoid dependency cycles, refactor a mixed root-package workspace, or decide between modules and crates. For in-crate src/ layout, see rust-module-layout.

Ir a la instalación

Datos de origen

Repositorio
full-stack-skills/rust-skills
Última actividad en el origen
11 de septiembre de 2026 a las 13:43
Idioma detectado de SKILL.md
inglés
Estrellas
5
Forks
1

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
22 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
rust-workspace
license
Apache-2.0
description
Design Rust project topology — single-crate packages, project-sized multi-crate workspaces (small root-flat, hybrid/domain-grouped, contained crates/, nested, or root-package layouts), workspace-level configuration, dependency DAGs, and crate-boundary decisions. Use when users ask how to split a project into crates, choose paths from the resulting project scale instead of copying a source-language module tree, configure a workspace, avoid dependency cycles, refactor a mixed root-package workspace, or decide between modules and crates. For in-crate src/ layout, see rust-module-layout.
# Rust Workspace and Project Topology > Authority: [Cargo Book — Workspaces](https://doc.rust-lang.org/cargo/reference/workspaces.html), [The Book ch7](https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html) and [ch14-03](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html), [Rust Reference ch7](https://doc.rust-lang.org/reference/items/modules.html), [RFC 1525](https://rust-lang.github.io/rfcs/1525-cargo-workspace.html), [matklad — Large Rust Workspaces](https://matklad.github.io/2021/08/22/large-rust-workspaces.html). This skill decides **how many crates a project should have and how they relate**. Its companion `rust-module-layout` decides **what lives inside one crate's `src/`**. ## Capability Boundaries ### ✅ Strengths 1. Single-crate vs multi-crate workspace decision (decision tree with five concrete triggers) 2. Project-driven workspace layouts: small root-flat, hybrid/domain-grouped, contained `crates/`, nested, and root-package 3. Virtual manifest vs root package — trade-offs and migration 4. Workspace-level shared configuration: `[workspace.package]`, `[workspace.dependencies]`, `[workspace.lints]` 5. Dependency direction DAGs — types → core → sdk → server → binary 6. Diagnosing and refactoring the "mixed root package" anti-pattern 7. Crate naming, publishing, and version coordination across members ### ⚠️ Prerequisites 1. Rust ownership and basic module syntax — see `rust-stable` 2. In-crate module layout (lib.rs facade, mod declarations, visibility) — see `rust-module-layout` ### ❌ Out of Scope 1. Cargo.toml `[dependencies]` syntax and feature resolution → use `rust-cargo-build` 2. In-crate src/ directory layout → use `rust-module-layout` 3. Rust syntax fundamentals → use `rust-stable` 4. Testing organization → use `rust-testing` ## When to Use - "Should I split this into a workspace or keep it one crate?" - "How do I configure a Cargo workspace?" - "Refactor my rbatis-style mixed root package" - "Why does cargo only build one crate at the root?" - "How do I share dependencies across workspace members?" - "Is my dependency direction correct?" ## Data Privacy This skill does not collect, store, or transmit any user data. --- # Foundations — Packages, Crates, Modules > In-crate module layout depth (mod files, visibility, re-exports) is covered by the companion `rust-module-layout` skill. This section covers only the parts that affect project-level decisions. ### Package vs crate vs workspace | Term | Meaning | |------|---------| | **Package** | One `Cargo.toml` and the source it points at; what you publish to crates.io | | **Crate** | A compilation unit — either a library (`src/lib.rs`) or a binary (`src/main.rs`, `src/bin/*.rs`). A package can contain multiple crates (1 lib + N binaries). | | **Workspace** | A collection of packages sharing one `Cargo.lock` and `target/`. | | **Module** | A nameable scope inside a crate — declared with `mod foo;`, resolved to `src/foo.rs` or `src/foo/mod.rs`. | ### Single-package layouts ```text # Library only # Binary only # Library + binary my-lib/ my-app/ my-crate/ ├── Cargo.toml ├── Cargo.toml ├── Cargo.toml └── src/ └── src/ └── src/ └── lib.rs └── main.rs ├── lib.rs └── main.rs # Multi-binary (one package, multiple binaries) my-app/ ├── Cargo.toml └── src/ ├── lib.rs ├── main.rs # binary named after the package └── bin/ ├── tool_a.rs # binary `tool_a` └── tool_b.rs # binary `tool_b` ``` ### Module declaration (the rule that surprises Java/Python devs) ```rust // src/lib.rs — declare modules explicitly; directories are NOT auto-discovered pub mod front_of_house; // loads src/front_of_house.rs OR src/front_of_house/mod.rs mod back_of_house; // private pub(crate) mod utils; // crate-visible ``` Everything is **private by default**; `pub` exposes. See `rust-module-layout` for the full visibility model, the parent-bound reachability rule, and the modern `foo.rs + foo/` layout. ### `use` paths (quick reference) ```rust use crate::front_of_house::hosting; // absolute (current crate) use std::collections::HashMap; // absolute (external) use self::back_of_house::Cook; // relative (current module) use super::parent_module::helper; // relative (parent) use std::{cmp::Ordering, io}; // nested use std::fmt::Result as FmtResult; // alias pub use crate::front_of_house::hosting; // re-export (facade pattern) ``` ### Conditional compilation ```rust #[cfg(target_os = "linux")] fn only_linux() {} #[cfg(feature = "serde")] fn with_serde() {} #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] struct Config; if cfg!(target_os = "linux") { /* runtime check */ } ``` # Project Scaffolding ```bash cargo new my-app # Binary project cargo new my-lib --lib # Library project cargo init # Initialize current directory ``` For multi-crate workspaces, create the root virtual manifest by hand (there is no `cargo workspace new`), choose the member topology from the decision model below, then run `cargo new --lib <selected-member-path>`. For a small root-flat workspace that may be `cargo new --lib my-core`; for a contained large workspace it may be `cargo new --lib crates/my-core`. For templates, use [`cargo-generate`](https://github.com/cargo-generate/cargo-generate). --- # Workspace Patterns (single-crate vs multi-crate) > Authority: [Cargo Book — Workspaces](https://doc.rust-lang.org/cargo/reference/workspaces.html), [The Book ch14-03](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html), [matklad — Large Rust Workspaces](https://matklad.github.io/2021/08/22/large-rust-workspaces.html), [RFC 1525](https://rust-lang.github.io/rfcs/1525-cargo-workspace.html). A **workspace** is a collection of one or more packages that share a single `Cargo.lock` and `target/` directory. Workspaces exist for four reasons: **faster builds** (shared dependency compilation), **coordinated versions** (one lockfile, one `cargo publish` flow), **cleaner boundaries** (each crate is an independent compilation unit with its own public API), and **independent reuse** (users can depend on one crate without pulling in the others). ## Decision tree — how many crates? ``` Is this project one cohesive library or one binary? ├── Yes → Single-crate layout (Section 8.1) └── No, it has multiple subdomains ├── Do the subdomains share types and call each other heavily? │ └── Single-crate with module directories (use rust-module-layout skill) └── At least one subdomain is independently useful / independently versioned / has different deps? └── Multi-crate workspace (Section 8.2) ``` **Rule of thumb**: split into crates only when at least one of these is true (from `references/production-workspace-boundaries.md`): 1. The subdomain requires independent publishing or third-party reuse 2. The subdomain needs distinct `feature` / target / `no_std` / WASM boundaries 3. You need to **prohibit** a reverse dependency at compile time (e.g., `core` must not depend on `tokio`) 4. The subdomain has an independently versioned public API 5. Test/build lifecycles are significantly different If none of these apply, **prefer modules over crates**. Modules are cheaper (no publish, no version coordination, no separate `Cargo.toml`). See the `rust-module-layout` skill for in-crate organization. ## Single-crate layout The default for small-to-medium projects. One `Cargo.toml`, one `src/`. Four variants: library only, binary only, library + binary (same package), or multi-binary (`src/bin/<name>.rs`). See `examples/single-crate.md` for all four skeletons. **When to graduate to a workspace**: when a binary or library needs an independent dependency/feature/target boundary, release trajectory, or build/test lifecycle. A second binary alone can remain under `src/bin/` in the same package. ## Multi-crate workspace — two flavors of root Cargo supports two kinds of root `Cargo.toml`: | Flavor | What's in root | When to use | |--------|---------------|-------------| | **Virtual manifest** | `[workspace]` only — **no `[package]`** | **Default for new workspaces.** Clean root, no top-level `src/`; members may be root-flat, hybrid, or contained. | | **Root package** | `[workspace]` **+** `[package]` + `src/` at root | Small (2-3 package) workspaces where one package is unambiguously primary; you accept the trade-offs below. | The Cargo team and community (notably matklad's [Large Rust Workspaces](https://matklad.github.io/2021/08/22/large-rust-workspaces.html)) recommend **virtual manifests** for any non-trivial workspace: 1. **Root pollution** — a `[package]` at the root forces `src/`, `tests/`, `benches/` into the top level alongside every other crate's directory. 2. **Command ergonomics** — with a root package, `cargo build` at the root builds *only* the root package; `--workspace` is needed for everything. Virtual manifests build all members by default. 3. **Publishing friction** — root package + members leads to confusing `cargo publish` ordering. ## Project-driven layout patterns | Pattern | Layout | When | Used by | |---------|--------|------|---------| | **A. Root-flat virtual** | `<member>/` beside root `Cargo.toml` | **Default for small cohesive workspaces**, usually 2-8 packages/members | [Tokio](https://github.com/tokio-rs/tokio), [Serde](https://github.com/serde-rs/serde), [Clap](https://github.com/clap-rs/clap) | | **B. Hybrid/domain-grouped** | core members at root plus `support/*`, `examples/*`, or another real family | Growing workspaces with stable families or noisy adapters/examples | framework and migration workspaces | | **C. Contained/grouped** | `crates/<member>/` or `crates/<category>/*` | Large workspaces, multi-language repositories, or roots that need a Rust container | [Bevy](https://github.com/bevyengine/bevy)-style large repositories | | **D. Nested sub-workspaces** | `vendor/<sub>/` with own `[workspace]` | Git submodule or vendored-workspace isolation (rare) | vendored upstream workspaces | | **E. Root package** | `[package]` at root plus sibling/contained members | 2-3 packages with one genuinely primary crate | library plus companion CLI | Workspace member count is a **signal, not a law**. Decide package/crate boundaries first, then consider repository root noise, independent publishing, adapter/plugin families, targets, examples/tests, other languages, and established paths. As a review trigger: 2-8 cohesive packages normally stay root-flat; around 8-20 compare root-flat with a hybrid; 20+ commonly benefits from grouping or `crates/`. Never create one Cargo package per Maven/Gradle module without a Rust boundary reason. Full skeletons and the decision matrix: `references/workspace-layouts.md`. ### Pattern A skeleton — small workspace default ```text my-project/ ├── Cargo.toml # virtual: [workspace] ├── my-core/ # library │ ├── Cargo.toml │ └── src/lib.rs ├── my-net/ # library │ └── ... └── my-cli/ # binary └── src/main.rs ``` ```toml # root Cargo.toml — virtual manifest [workspace] resolver = "3" members = ["my-core", "my-net", "my-cli"] [workspace.package] edition = "2024" version = "0.1.0" license = "Apache-2.0" [workspace.dependencies] serde = { version = "1", features = ["derive"] } # pin once my-core = { path = "my-core" } # internal deps here ``` ```toml # my-net/Cargo.toml [package] name = "my-net" version.workspace = true # inherits from [workspace.package] edition.workspace = true [dependencies] serde.workspace = true # inherits from [workspace.dependencies] my-core.workspace = true # internal workspace dep ``` ## The "mixed root package" anti-pattern A common mistake from developers coming from Java/Maven or Python: putting a real `[package]` with substantial code at the workspace root **while also** having member crates. ```toml # Cargo.toml — DON'T (anonymized from a real public crate) [workspace] members = ["codegen", "macro-driver", "example"] [package] # ❌ root package + workspace together name = "my_framework" version = "4.9.6" [dependencies] my-codegen = { path = "codegen" } my-macro-driver = { path = "macro-driver" } ``` ```text my-framework/ ├── Cargo.toml # 163 lines mixing workspace + package config ├── src/ # ❌ the root package's source pollutes the root │ ├── lib.rs # 30 lines of `pub use *::*` │ ├── crud.rs # 591 lines (monster file — see rust-module-layout) │ ├── executor.rs # 642 lines │ └── plugin/ ├── codegen/ # member crate ├── macro-driver/ # member crate └── tests/ ``` **Five problems**: root pollution, asymmetric commands (`cargo build` builds only the root), coupled versioning, no clear facade (lib.rs becomes a glob re-export hub), and migration is a breaking change once published. **Refactor target** — virtual manifest with all crates under `crates/`: ```text my-framework/ ├── Cargo.toml # virtual manifest — workspace config only └── crates/ ├── my-framework/ # was: src/ at root │ ├── Cargo.toml │ └── src/ ├── codegen/ ├── macro-driver/ └── example/ ``` Migration is **mechanical and preserves the published crate name** — full six-step procedure in `references/mixed-root-package-antipattern.md` and a worked example in `examples/migration-mixed-to-virtual.md`. ## Workspace-level shared configuration
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub