---
title: Rust (Experimental)
description: Use experimental native Cargo workspace support with Turborepo.
product: turborepo
type: integration
summary: Discover Cargo workspace crates as Turborepo packages and run native Cargo tasks.
prerequisites:
  - /docs/crafting-your-repository/structuring-a-repository
  - /docs/crafting-your-repository/configuring-tasks
related:
  - /docs/guides/multi-language
  - /docs/crafting-your-repository/caching
  - /docs/crafting-your-repository/running-tasks
---

# Rust (Experimental)



Turborepo can discover packages across languages and toolchains. It can discover the crates in [a Cargo workspace](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) as packages, add their dependency relationships to the Package Graph, and map common Turborepo tasks to Cargo commands. Cargo remains responsible for compiling crates and scheduling their internal build graph.

<Callout type="warn">
  Cargo workspace support is experimental and may change. Use a version of
  `turbo` that recognizes `experimentalCargoWorkspaces` everywhere the
  repository runs, including local hooks and CI. Older versions reject unknown
  future flags.
</Callout>

## Enable Cargo workspaces

Set the future flag in the root `turbo.json`:

```json title="./turbo.json"
{
  "$schema": "https://turborepo.dev/schema.json",
  "futureFlags": {
    "experimentalCargoWorkspaces": true
  },
  "tasks": {}
}
```

## Prerequisites

To work with Rust, the repository root must contain:

* `turbo`, `cargo`, and `rustc` available on `PATH`
* A root `Cargo.toml` containing a virtual Cargo workspace
* A valid, unique `[workspace.metadata] name`, used for a synthetic Turborepo package that represents the Cargo workspace
* A root `Cargo.lock`

```toml title="./Cargo.toml"
[workspace]
members = ["crates/*"]
resolver = "2"

[workspace.metadata]
name = "acme-rust"
```

<Callout type="info">
  The root `Cargo.toml` cannot also define a `[package]`. Move a root crate into
  a subdirectory and include it in `[workspace].members`.
</Callout>

### Repository structure

In the example above, members are defined as `crates/*`. Cargo then uses every `crates/*/Cargo.toml` as a package in the workspace. Each package is added to Turborepo's understanding of your repository.

Cargo packages come in two types:

* **Entrypoint crates** have a `bin`, `cdylib`, or `staticlib` target. They can be used as targets for [`--filter`](/docs/reference/run#--filter-string) and [`affected`](/docs/reference/run#--affected) calculations.
* **Library crates** remain Package Graph nodes so filtering and affectedness calculations follow Rust dependency relationships. Their native tasks are no-ops because Cargo builds libraries as part of an entrypoint's dependency closure.
* **The root workspace package** uses `[workspace.metadata] name`, depends on every member crate, and runs workspace-wide verification tasks.

## Built-in tasks

Turborepo implicitly registers these task mappings, since they are common to all Cargo workspaces.

| Package                                  | Turbo task       | Cargo command                            |
| ---------------------------------------- | ---------------- | ---------------------------------------- |
| Entrypoint crate                         | `build`          | `cargo build --package=<crate> --locked` |
| Entrypoint crate with exactly one binary | `run`, `dev`     | `cargo run --package=<crate> --locked`   |
| Workspace package                        | `test`           | `cargo test --workspace --locked`        |
| Workspace package                        | `check`          | `cargo check --workspace --locked`       |
| Workspace package                        | `lint`, `clippy` | `cargo clippy --workspace --locked`      |
| Workspace package                        | `bench`          | `cargo bench --workspace --locked`       |
| Workspace package                        | `doc`, `docs`    | `cargo doc --workspace --locked`         |

### Pass Cargo flags

Arguments after Turborepo's `--` are passed to the mapped command. Build flags that select a supported output layout participate in hashing and automatic output detection:

```bash title="Terminal"
turbo run build --filter=rust-api -- --release
turbo run build --filter=rust-api -- --profile=ci
turbo run build --filter=rust-api -- --target=aarch64-unknown-linux-gnu
turbo run build --filter=rust-api -- --target-dir=target-ci
```

`check` and `doc` also accept Cargo flags directly after `--`. For `run`, `test`, `bench`, and `lint`/`clippy`, Turborepo inserts Cargo's second `--`, so the arguments go to the binary, test or bench harness, or Clippy rather than Cargo itself. For example:

```bash title="Terminal"
turbo run run --filter=rust-api -- --port 8080
turbo run test --filter=acme-rust -- --nocapture
```

## Custom tasks

To define a Cargo-backed task that is not built in, or replace a built-in mapping, enable `experimentalTaskCommand` and set the task's `command`. The command is an argument array that runs directly without a shell. For example, the synthetic workspace package can add a `format` task and make `lint` reject warnings:

```json title="./turbo.json"
{
  "$schema": "https://turborepo.dev/schema.json",
  "futureFlags": {
    "experimentalCargoWorkspaces": true,
    "experimentalTaskCommand": true
  },
  "tasks": {
    "acme-rust#format": {
      "command": ["cargo", "fmt", "--all", "--", "--check"]
    },
    "acme-rust#lint": {
      "command": [
        "cargo",
        "clippy",
        "--workspace",
        "--locked",
        "--",
        "-D",
        "warnings"
      ]
    }
  }
}
```

## Filtering, affected packages, and queries

You can use the Cargo workspace or packages as entrypoints for [filters](/docs/reference/run#--filter-string):

```bash title="Terminal"
# Execute builds for all toolchains
turbo run build

# Build all crates
turbo run build --filter=my-cargo-workspace

# Build a package
turbo run build --filter=my-cargo-binary

# Build affected packages
turbo run build --affected
```

Additionally, [`turbo query`](/docs/reference/query) can be used to understand your repository's graphs and more.

## Caching behavior

With zero configuration, Turborepo creates task hashes using:

* The entrypoint and its transitive local crate source files
* Root Cargo files
* Relevant Cargo and compiler environment
* The resolved external dependency closure from `Cargo.lock`
* The complete `rustc -vV` identity.

Project-specific hashing inputs must be accounted for manually. This includes:

* Environment variables must read by build scripts still need to be declared in the task's [`env`](/docs/reference/configuration#env) configuration
* File inputs that are not included by default. Use [`inputs`](/docs/reference/configuration#inputs) to define your own file inputs and [`$TURBO_DEFAULT$`](/docs/reference/configuration#turbo_default) to preserve zero-configuration file inputs

### Output caching

Automatic output caching stores only the exact final `bin`, `cdylib`, and `staticlib` deliverables. It does not cache Cargo's incremental `target/` state or use profile or target wildcards.

Turborepo resolves exact outputs for these layouts:

* The default `debug` profile, `--release`, built-in profile aliases, and ordinary custom `--profile=<name>` directories
* The host target, a supported `--target=<triple>`, or a supported `CARGO_BUILD_TARGET`
* An in-repository target directory selected, in order, by `--target-dir`, `CARGO_TARGET_DIR`, or Cargo metadata, including repository `.cargo/config` `target-dir`

Target-specific builds include the target triple in the artifact path. A target directory must stay within the repository after resolving existing path components.

When Turborepo cannot prove the exact output layout, it disables implicit task caching rather than creating a log-only cache hit that could skip compilation. Examples include unknown output-affecting flags, repository `build.target`, unsupported or custom target triples, escaping target directories, compiler overrides, artifact directory overrides, custom profile directory names, and output-altering unstable manifest features. Explicit `outputs` or an explicit `cache` setting represent user authority and override this output-unavailability fallback. Only opt in when your configuration fully describes the outputs.

Untracked inputs are more strict. External, included, or symlinked Cargo configuration can affect a build without participating safely in its hash, so Turborepo disables caching unless `cache` itself is explicitly configured. Explicit `outputs` alone do not make untracked inputs safe.

## Limitations

* Only the root virtual Cargo workspace is discovered. Root crates are rejected, nested workspaces are not independently discovered, and resolved external or non-member local path dependencies fail closed.
* `cdylib`/`staticlib`-only entrypoints are build-only. Multi-bin crates require both `package.default-run` and an authored task definition for native `run` and `dev`; pass-through arguments cannot supply Cargo's `--bin` option.
* Verification tasks always run for the whole Cargo workspace through the synthetic package, not per crate.
* Automatic artifact caching is deliberately disabled for unresolved output layouts and untracked inputs. Explicit outputs can authorize an unresolved output layout, but untracked inputs require an explicit `cache` setting. Cargo's full `target/` directory is never a task output.
* The synthetic workspace package has no directory and cannot be passed to `turbo prune`; prune an entrypoint crate instead.
* In watch mode, a no-op save in a crate may rerun a fast cache check because content-hash deduplication is still based on JavaScript workspace globs.
* Build-script-specific environment and files outside crate directories are not inferred. Declare them with `env` and additive `inputs` as needed.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)