dcf3a897d1
Rust-on-NixOS knowledge plugin. Relocates plugin.json into .claude-plugin/ so it loads as a proper plugin, and adds README/LICENSE/.gitignore for distribution. Skill rust-nix-toolchain (Cranelift vs LLVM codegen, fenix nightly pins, cargo/clippy gates) with cranelift-limitations reference.
139 lines
4.3 KiB
Markdown
139 lines
4.3 KiB
Markdown
---
|
|
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/<hostname>
|
|
nix flake update fenix
|
|
sudo nixos-rebuild switch --flake '.#<hostname>'
|
|
```
|
|
|
|
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 = "<manifest-hash>"; # 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
|
|
};
|
|
```
|