Setting up Rust: rustup, Cargo and edition 2024

Installing with rustup, cargo new and cargo add, the Cargo.toml fields that matter, cargo check, run, test and fmt, clippy, and when a workspace helps.

rustup and toolchains

Rust is installed through rustup, a version manager rather than a package. It installs the toolchain — rustc, cargo, the standard library — and lets you switch versions per project, which is why a 2021-edition crate and a 2024-edition crate can live side by side.

rustup show                       # installed toolchains and the active one
rustup update                     # upgrade every channel
rustup toolchain install nightly  # opt in to a channel per project
rustup component add clippy rustfmt
rustup target add x86_64-unknown-linux-musl

rustup override set 1.85.0        # pin this directory to a version
rustc --version && cargo --version
ChannelWhat it is
stableThe default. A new release every six weeks; what you ship against
betaThe next stable, useful for checking a release will not break you
nightlyUnstable features behind feature gates; needed by some tooling, never required to ship
rust-toolchain.tomlA file in the repository pinning the exact channel and components for everyone
[toolchain]
channel = "1.85.0"
components = ["rustfmt", "clippy"]
targets = ["x86_64-unknown-linux-musl"]

Cargo.toml: the manifest

cargo new notes --bin      # binary crate; --lib for a library
cargo new --lib notes-core
cargo add clap --features derive
cargo add --dev tempfile
cargo add [email protected]
[package]
name = "notes"
version = "0.1.0"
edition = "2024"          # the language version: 2015, 2018, 2021 or 2024
rust-version = "1.85"     # the minimum toolchain you support
description = "A small note manager"
license = "MIT OR Apache-2.0"

[dependencies]
clap = { version = "4", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
thiserror = "2"

[dev-dependencies]
tempfile = "3"

[profile.release]
lto = "thin"
strip = "symbols"
  • edition is not the compiler version; it is the language rules your crate is parsed and checked under. Dependencies use their own edition, so upgrading yours never breaks them.
  • rust-version is a promise to users: below it, Cargo reports a clear error instead of a compile failure deep inside your code.
  • A caret requirement such as "1.2" allows any 1.x at or above 1.2. Semver is treated as a guarantee, so an incompatible change must be a new major version.
  • Cargo.lock pins exact versions and belongs in version control for binaries. For libraries it is still committed, and CI can ignore it with cargo update to test against newer compatible releases.
  • [profile.release] settings change speed, size and build time. lto = "thin" is a good default; codegen-units = 1 and full lto are slow to build and worth it only for final artifacts.

The daily workflow

cargo check            # type-check only: seconds instead of minutes
cargo run -- --help
cargo test
cargo test --doc
cargo fmt --all
cargo clippy --all-targets -- -D warnings
cargo doc --no-deps --open
cargo tree -d          # find duplicated dependency versions
cargo publish --dry-run
  • Run cargo check while editing and cargo build only when you need a binary. The optimiser and code generator are the slow part and neither is needed to find a type error.
  • cargo clippy catches real mistakes, not just style: a redundant clone, a match that is really an if let, a comparison that could be PartialEq. Treat its warnings as errors in CI.
  • Editor support comes from rust-analyzer, which uses the same check pipeline; inlay hints make inferred types visible without an annotation.
  • Add a workspace when two crates need to be built together or share one lock file. It is a [workspace] table with a members list, plus shared dependency versions under [workspace.dependencies].
  • Do not fight the borrow checker through an editor plugin that rewrites your code for you. Learn the error, then fix the design — the fix is usually a smaller scope or an explicit clone.
💡
The fastest way to learn Rust is a build loop that stays under a few seconds: cargo check on save, cargo test when a unit of work is done, clippy before you commit. Compile errors are the tutor, and they cost nothing to read if you do not let them pile up.

FAQ

Should I use nightly?
Only when a specific tool needs it. Stable builds everything you ship; nightly is for feature-gated experiments and some formatters and linters. Pin it in rust-toolchain.toml if you do, so everyone gets the same one.
Why does cargo check pass while my editor shows errors?
The editor's rust-analyzer has its own cache and may be running against a different target or feature set. Run cargo check --all-targets to see the truth, and restart the language server if the two stay out of sync.

Modules, crates and project structure Testing, documentation and CI

Last refreshed 2026-09-18.