--- name: rust-nix-toolchain description: >- This skill should be used when the user encounters "asm! and global_asm! sym operands are not yet supported", "wasmtime-fiber" compilation failures, "cargo test fails but cargo check works", "cranelift codegen backend" issues, "update Rust nightly on NixOS", "fenix toolchain pin", or mentions Rust codegen backend problems on NixOS. Covers the Cranelift vs LLVM codegen backend split, NixOS fenix toolchain management, and common Rust nightly pitfalls. --- # Rust Nightly Toolchain on NixOS ## Cranelift vs LLVM Codegen Backend NixOS dev setups commonly use `codegen-backend = "cranelift"` in `~/.cargo/config.toml` for faster dev builds. **Cranelift does NOT support `global_asm! { sym }` operands.** This causes `wasmtime-fiber` and other crates using `global_asm!` with `sym` to fail. ### Symptom ``` error: asm! and global_asm! sym operands are not yet supported --> wasmtime-fiber-28.0.1/src/stackswitch/x86_64.rs ``` ### Why `cargo check` works but `cargo test` fails - `cargo check` / `cargo clippy` only verify types and borrow checking — **no codegen** - `cargo test` / `cargo build` invoke the codegen backend, hitting the Cranelift limitation - This makes the error appear only at test/build time, not during linting ### Fix: Override codegen backend for tests ```bash # One-shot: force LLVM for this test run CARGO_PROFILE_DEV_CODEGEN_BACKEND=llvm cargo test --lib # Or permanently in the project's .cargo/config.toml: # [profile.dev] # codegen-backend = "llvm" ``` ### Where the Cranelift backend is configured Check `~/.cargo/config.toml`: ```toml [profile.dev] codegen-backend = "cranelift" # faster codegen for dev builds [unstable] codegen-backend = true ``` This is a **user-level** config that affects all projects. Project-level `.cargo/config.toml` can override it. ## NixOS Fenix Toolchain Management ### How the Rust nightly is pinned On NixOS with fenix, the Rust nightly version is determined by: 1. **`flake.nix`** declares fenix input: `fenix.url = "github:nix-community/fenix"` 2. **`flake.lock`** pins a specific fenix commit, which bundles nightly manifests 3. **NixOS module** (e.g. `nixos/rust-dev.nix`) selects components: ```nix rust-nightly = fenix.packages.${pkgs.stdenv.hostPlatform.system}.complete.withComponents [ "cargo" "clippy" "rustc" "rustfmt" "rust-src" "rust-analyzer" "rustc-codegen-cranelift-preview" ]; ``` ### Updating the nightly ```bash cd ~/projects/servers/ nix flake update fenix sudo nixos-rebuild switch --flake '.#' ``` This pulls the latest fenix commit which includes the most recent nightly. ### Pinning a specific nightly date Replace `complete.withComponents` with `toolchainOf`: ```nix rust-nightly = fenix.packages.${system}.toolchainOf { channel = "nightly"; date = "2026-03-15"; sha256 = ""; # required for pure evaluation }.withComponents [ "cargo" "clippy" "rustc" "rustfmt" "rust-src" ]; ``` ## Cargo Quality Gate Standard Rust quality gate sequence: ```bash # 1. Format cargo fmt # 2. Lint (zero warnings required) cargo clippy --all --benches --tests --examples --all-features # 3. Test (use LLVM if Cranelift is default) CARGO_PROFILE_DEV_CODEGEN_BACKEND=llvm cargo test --lib ``` ### Common clippy patterns | Warning | Fix | |---------|-----| | `collapsible_match` / `collapsible_if` | Merge nested `if` into match guard: `Variant if condition => { ... }` | | `new_without_default` | Add `impl Default for T { fn default() -> Self { Self::new() } }` | | `let_unit_value` | Remove `let _ =` from expressions returning `()` | | `unnecessary_sort_by` | Use `sort_by_key(|x| Reverse(x.field))` instead of `sort_by(|a, b| b.field.cmp(&a.field))` | | `derivable_impls` | Replace manual `impl Default` with `#[derive(Default)]` | | `manual_map` | Replace `if let Some(x) = opt { Some(f(x)) } else { None }` with `opt.map(f)` | | `useless_conversion` | Remove redundant `.into_iter()` on values already implementing `IntoIterator` | ### Feature-gated fields in tests When a struct has `#[cfg(feature = "...")]` fields, tests compiled with `--all-features` must include those fields: ```rust let cfg = MyConfig { normal_field: value, #[cfg(feature = "discord")] discord: None, // must be present when feature is active }; ```