
Rust
- 8 installs
- 33 repo stars
- Updated April 26, 2026
- bighardperson/computer-science-skills-collection
rust is a Claude skill that helps write idiomatic Rust and avoid ownership, borrow-checker, lifetime, and concurrency pitfalls.
About
This skill helps write idiomatic Rust by naming the common ownership, borrowing, lifetime, and concurrency pitfalls and their fixes. A developer uses it when writing or debugging Rust and fighting the borrow checker or confusing compiler errors. It maps traps to reference files and offers a compiler-error lookup table.
- Reference for writing idiomatic Rust and avoiding ownership, borrow-checker, and lifetime traps
- Tables of high-frequency failures across ownership, strings, errors, iterators, concurrency, and memory
- Includes a common-compiler-errors table and Cargo traps
Rust by the numbers
- 8 all-time installs (skills.sh)
- Ranked #88 of 121 Rust skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
Rust capabilities & compatibility
Free; needs local rustc and cargo, no API key
- Capabilities
- debugging · refactoring
- Use cases
- debugging · refactoring
- Platforms
- Linux · macOS · Windows
- Pricing
- Free
What Rust says it does
Write idiomatic Rust avoiding ownership pitfalls, lifetime confusion, and common borrow checker battles.
**`s[0]` doesn't compile** — use `.chars().nth(0)` or `.bytes()`
**`Rc` is NOT `Send`** — use `Arc` for threads
npx skills add https://github.com/bighardperson/computer-science-skills-collection --skill rustAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 33 |
| Last updated | April 26, 2026 |
| Repository | bighardperson/computer-science-skills-collection ↗ |
What it does
Write idiomatic Rust and resolve ownership, borrow-checker, lifetime, and concurrency pitfalls.
When should I use this skill?
Writing or debugging Rust and fighting the borrow checker, lifetimes, or confusing compiler errors.
By the numbers
- 6-row common compiler errors table
- 5 reference files (ownership-borrowing, types-strings, errors-iteration, concurrency-memory, advanced-traps)
Files
Quick Reference
| Topic | File | Key Trap |
|---|---|---|
| Ownership & Borrowing | ownership-borrowing.md | Move semantics catch everyone |
| Strings & Types | types-strings.md | String vs &str, UTF-8 indexing |
| Errors & Iteration | errors-iteration.md | unwrap() in production, lazy iterators |
| Concurrency & Memory | concurrency-memory.md | Rc not Send, RefCell panics |
| Advanced Traps | advanced-traps.md | unsafe, macros, FFI, performance |
---
Critical Traps (High-Frequency Failures)
Ownership — #1 Source of Compiler Errors
- Variable moved after use — clone explicitly or borrow with
& - `for item in vec` moves vec — use
&vecor.iter()to borrow - `String` moved into function — pass
&strfor read-only access
Borrowing — The Borrow Checker Always Wins
- Can't have `&mut` and `&` simultaneously — restructure or interior mutability
- Returning reference to local fails — return owned value instead
- Mutable borrow through `&mut self` blocks all access — split struct or
RefCell
Lifetimes — When Compiler Can't Infer
- `'static` means CAN live forever, not DOES —
Stringis 'static capable - Struct with reference needs `<'a>` —
struct Foo<'a> { bar: &'a str } - Function returning ref must tie to input —
fn get<'a>(s: &'a str) -> &'a str
Strings — UTF-8 Surprises
- `s[0]` doesn't compile — use
.chars().nth(0)or.bytes() - `.len()` returns bytes, not chars — use
.chars().count() - `s1 + &s2` moves s1 — use
format!("{}{}", s1, s2)to keep both
Error Handling — Production Code
- `unwrap()` panics — use
?ormatchin production - `?` needs `Result`/`Option` return type — main needs
-> Result<()> - `expect("context")` > `unwrap()` — shows why it panicked
Iterators — Lazy Evaluation
- `.iter()` borrows, `.into_iter()` moves — choose carefully
- `.collect()` needs type —
collect::<Vec<_>>()or typed binding - Iterators are lazy — nothing runs until consumed
Concurrency — Thread Safety
- `Rc` is NOT `Send` — use
Arcfor threads - `Mutex` lock returns guard — auto-unlocks on drop, don't hold across await
- `RwLock` deadlock — reader upgrading to writer blocks forever
Memory — Smart Pointers
- `RefCell` panics at runtime — if borrow rules violated
- `Box` for recursive types — compiler needs known size
- Avoid `Rc<RefCell<T>>` spaghetti — rethink ownership
---
Common Compiler Errors (NEW)
| Error | Cause | Fix |
|---|---|---|
value moved here | Used after move | Clone or borrow |
cannot borrow as mutable | Already borrowed | Restructure or RefCell |
missing lifetime specifier | Ambiguous reference | Add <'a> |
the trait bound X is not satisfied | Missing impl | Check trait bounds |
type annotations needed | Can't infer | Turbofish or explicit type |
cannot move out of borrowed content | Deref moves | Clone or pattern match |
---
Cargo Traps (NEW)
- `cargo update` updates Cargo.lock, not Cargo.toml — manual version bump needed
- Features are additive — can't disable a feature a dependency enables
- `[dev-dependencies]` not in release binary — but in tests/examples
- `cargo build --release` much faster — debug builds are slow intentionally
{
"ownerId": "kn73vp5rarc3b14rc7wjcw8f8580t5d1",
"slug": "rust",
"version": "1.0.1",
"publishedAt": 1771100339940
}{
"slug": "rust",
"name": "Rust",
"version": "1.0.1",
"installedAt": 1776152385671,
"source": "skillhub"
}Advanced Traps — unsafe, macros, FFI, testing, performance
Unsafe Code
- `unsafe` doesn't disable borrow checker — only allows 5 specific operations
- *Raw pointers `const T
/mut T`* — can be null, dangling, or aliased - `unsafe impl` for Send/Sync — you guarantee invariants compiler can't check
- `transmute` is nuclear — reinterprets bits, can cause UB easily
- Undefined behavior is NEVER acceptable — even if "it works on my machine"
Macro Pitfalls
- `macro_rules!` hygiene — identifiers don't leak, but paths can be tricky
- Macro expansion order — can cause surprising errors
- `$crate` for paths in macros — ensures correct crate resolution
- Proc macros need separate crate —
proc-macro = truein Cargo.toml - Debug macros with `cargo expand` — see what code actually generates
- `stringify!` and `concat!` — compile-time string operations
FFI Issues
- `#[repr(C)]` for C-compatible layout — Rust default layout is unspecified
- Null-terminated strings —
CString/CStrnotString/&str - `extern "C"` for C ABI — Rust ABI is unstable
- Ownership across FFI — who frees what? Document clearly
- Panics across FFI boundary — undefined behavior, use
catch_unwind - `Option<&T>` is nullable pointer — FFI-safe optimization
Testing Traps
- `#[cfg(test)]` module not in release — but dependencies still compile
- `assert_eq!` shows both values — better than
assert!(a == b) - `#[should_panic]` for panic tests — can specify
expected = "message" - `Result<(), E>` return in tests — use
?in test functions - Integration tests in `tests/` — separate compilation, external API only
- `cargo test -- --nocapture` — to see println! output
Performance Traps
- `.clone()` is not free — deep copy for most types
- String allocation on every `format!` — reuse buffers with
write! - `Vec` reallocation — use
with_capacity()if size known - Iterator vs loop — usually same perf, but check with
cargo bench - `Box<dyn Trait>` indirection — generics are faster if possible
- `#[inline]` across crates — needed for cross-crate inlining
- Debug vs Release — 10-100x difference, always bench in release
Concurrency & Memory Patterns
Concurrency
- Data shared between threads needs `Send` and `Sync` — most types are,
Rcis not - Use `Arc` for shared ownership across threads —
Rcis single-threaded only - `Mutex<T>` for mutable shared state — lock returns guard, auto-unlocks on drop
- `RwLock` allows multiple readers or one writer — deadlock if reader tries to write
- Async functions return `Future` — must be awaited or spawned
Memory Patterns
- `Box<T>` for heap allocation — also needed for recursive types
- `Rc<T>` for shared ownership (single-thread) —
Arc<T>for multi-thread - `RefCell<T>` for interior mutability — runtime borrow checking, panics on violation
- `Cell<T>` for Copy types interior mutability — no borrow, just get/set
- Avoid `Rc<RefCell<T>>` spaghetti — rethink ownership structure
Async Traps (NEW)
- `.await` only in async context — can't call from sync code directly
- Async traits need `async-trait` crate — or
-> impl Future(nightly/2024+) - `Mutex` guard across `.await` — use
tokio::sync::Mutexnotstd::sync::Mutex - `spawn` requires `'static` — move data in or use
Arc - Executor required —
tokio,async-std, orsmolto actually run futures - `select!` cancellation — dropped future may not run cleanup
Additional Memory Traps (NEW)
- `Weak<T>` for breaking cycles —
Rc/Arccycles leak memory - `Pin<T>` for self-referential — async futures are often pinned
- `MaybeUninit` for uninitialized — safe wrapper for unsafe init patterns
- `std::mem::drop` vs `Drop` trait —
drop(x)just callsx.drop()early - `ManuallyDrop` skips destructor — useful for FFI or unions
- Stack overflow with deep recursion — Box recursion or increase stack
Error Handling & Pattern Matching & Iterators
Error Handling
- `unwrap()` panics on None/Err — use
?operator ormatchin production - `?` requires function returns Result/Option — can't use in main without
-> Result<()> - Converting errors: `map_err()` or `From` trait implementation
- `expect("msg")` better than `unwrap()` — shows context on panic
- `Option` and `Result` don't mix — use
.ok()or.ok_or()to convert
Pattern Matching
- Match must be exhaustive — use
_wildcard for remaining cases - `if let` for single pattern — avoids verbose match for one case
- Guard conditions: `match x { n if n > 0 => ... }` — guards don't create bindings
- `@` bindings: `Some(val @ 1..=5)` — binds matched value to name
- `ref` keyword in patterns to borrow — often unnecessary with match ergonomics
Iterator Gotchas
- `.iter()` borrows, `.into_iter()` moves, `.iter_mut()` borrows mutably
- `.collect()` needs type annotation —
collect::<Vec<_>>()or let binding with type - Iterators are lazy — nothing happens until consumed
- `.map()` returns iterator, not collection — chain with
.collect() - Modifying while iterating impossible — collect indices first, then modify
Additional Traps (NEW)
Error Handling
- `anyhow` vs `thiserror` — anyhow for apps, thiserror for libraries
- `?` in closures — closure must also return Result/Option
- `panic!` unwinds by default —
panic = "abort"in Cargo.toml for smaller binary
Iterators
- `.filter_map()` combines filter and map — cleaner than chaining
- `.flatten()` for nested iterators —
OptionandResultare iterators too - `.cloned()` vs `.copied()` — copied for Copy types, cloned calls clone
- `Iterator` vs `IntoIterator` — for loops call
into_iter()automatically - `.enumerate()` gives `(index, value)` — index is usize
Ownership & Borrowing & Lifetimes
Ownership Traps
- Variable moved after use — clone explicitly or borrow with
& - `for item in vec` moves vec — use
&vecor.iter()to borrow - Struct field access moves field if not Copy — destructure or clone
- Closure captures by move with `move ||` — needed for threads and 'static
- `String` moved into function — pass
&strfor read-only access
Borrowing Battles
- Can't have mutable and immutable borrow simultaneously — restructure code or use interior mutability
- Borrow lasts until last use (NLL) — not until scope end in modern Rust
- Returning reference to local fails — return owned value or use lifetime parameter
- Mutable borrow through `&mut self` blocks all other access — split struct or use
RefCell
Lifetime Gotchas
- Missing lifetime annotation — compiler usually infers, explicit when multiple references
- `'static` means "can live forever", not "lives forever" —
Stringis 'static,&strmay not be - Struct holding reference needs lifetime parameter —
struct Foo<'a> { bar: &'a str } - Function returning reference must tie to input lifetime —
fn get<'a>(s: &'a str) -> &'a str
Additional Traps (NEW)
- Partial moves in structs — moving one field makes whole struct unusable (unless using remaining fields explicitly)
- `Option<&T>` vs `&Option<T>` —
.as_ref()converts outer to inner reference - Reborrowing `&mut` through `&` — auto-reborrow works but explicit sometimes needed
- Lifetime elision rules —
fn foo(x: &str) -> &strimplicitly ties output to input - `'a: 'b` means 'a outlives 'b — covariance/contravariance matters in generics
Strings & Type System
String Confusion
- `String` is owned, `&str` is borrowed slice — convert with
.as_str()orString::from() - Indexing `s[0]` fails — UTF-8 variable width, use
.chars().nth(0)or.bytes() - Concatenation: `s1 + &s2` moves s1 — use
format!("{}{}", s1, s2)to keep both - `.len()` returns bytes, not characters — use
.chars().count()for char count
Type System Traps
- Orphan rule: can't impl external trait on external type — newtype pattern workaround
- Trait objects `dyn Trait` have runtime cost — generics monomorphize for performance
- `Box<dyn Trait>` for heap-allocated trait object —
&dyn Traitfor borrowed - Associated types vs generics — use associated when one impl per type
- `Self` vs `self` — type vs value:
Self::new()vs&self
Additional String Traps (NEW)
- `&String` auto-derefs to `&str` — but prefer
&strin function params - `str::from_utf8` can fail — use
String::from_utf8_lossyif uncertain - `char` is 4 bytes (Unicode scalar) — not 1 byte like C
- `.split()` returns iterator —
collect()to getVec<&str> - `OsString` for paths — not all paths are valid UTF-8
Type System Advanced (NEW)
- `impl Trait` vs `dyn Trait` — static dispatch vs dynamic, different use cases
- `Sized` bound implicit —
?Sizedto accept unsized types - Coherence rules — only one impl per type, foundational trait first
- `PhantomData<T>` — for unused type parameters (e.g., lifetime markers)
- `Deref` coercion —
&Stringto&strautomatic, but can be confusing
Related skills
FAQ
Why does String indexing like s[0] not compile in Rust?
Because String is UTF-8; use .chars().nth(0) or .bytes() instead.
When should I use Arc instead of Rc?
Rc is NOT Send, so use Arc for sharing across threads.