Files
Oleks dcf3a897d1 Initial commit: rust-craft plugin
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.
2026-06-01 11:28:48 +03:00

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
};
```