Skip to main content
rule

rust · sanjeed5 — Cursor rule: what it does, how to install

A Cursor rule for Rust projects covering module layout, type-safe patterns, error handling, collections, and tests. It applies across project files.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/rust.mdc
Open on GitHub

What rust does

This Cursor rule guides Rust code organization around features rather than separate files for types and implementations. Related structs and their impl blocks stay together, large data types are split into focused structs, and newtypes keep different identifiers from being mixed. It also recommends the Builder pattern for complex construction and keeps generic bounds on the implementations or functions that need them.

For collection choices, the rule starts with Vec and HashMap, and asks developers to reserve alternatives for a measured bottleneck or a specific requirement. Recoverable failures should use Result; panic! is reserved for unrecoverable programming errors. Every unsafe block needs a preceding // SAFETY: explanation. Tests use #[test], while rustdoc examples are compiled as tests. A missing // SAFETY: comment conflicts with the rule.

What it helps with

  • organize Rust modules by feature
  • split oversized data structures
  • prevent swapped identifier arguments
  • pre-allocate collections when size is known
  • cover Rust code with unit and rustdoc tests

Example prompts for the agent

  • Review this Rust module and suggest a feature-based structure.
  • Replace mixed u64 identifiers with newtypes and update the call sites.
  • Check error handling, unsafe blocks, and #[test] coverage.

How to install rust

Manually: put the files in place

.cursor/rules/rust.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about rust

How do I install rust in Cursor?

Place the rule file at .cursor/rules/rust.mdc in the project.

What does the rust rule do?

It sets Rust guidance for module structure, newtypes, Builder construction, generic bounds, collections, Result, unsafe, and tests.

What must accompany an unsafe block?

A preceding // SAFETY: comment must explain why the operation is sound and preserves Rust safety invariants.

Source text

rust.mdc · CC0-1.0
---
description: Definitive guidelines for writing idiomatic, performant, and maintainable Rust code, focusing on modern best practices for structure, patterns, and error handling.
globs: **/*
---
# Rust Best Practices

This guide outlines our team's definitive best practices for writing Rust code. Adhere to these principles to ensure consistency, performance, and maintainability across all projects.

## 1. Code Organization and Structure

### 1.1 Module Structure: Feature-Driven, Not File-Type-Driven
Organize modules by *feature or functionality*, not by separating types from implementations. Keep related structs, enums, and their `impl` blocks together within the same module.

❌ BAD:
```rust
// types.rs
pub struct User { pub id: u64 }
// impls.rs
impl User {
    pub fn new(id: u64) -> Self { Self { id } }
}
```

✅ GOOD:
```rust
// user.rs (or a user/mod.rs)
pub struct User { pub id: u64 }
impl User {
    pub fn new(id: u64) -> Self { Self { id } }
    pub fn get_id(&self) -> u64 { self.id }
}
```

### 1.2 Small, Cohesive Structs
Design structs to be small and focused. Break down complex data into multiple smaller, composable structs. This improves borrow-checker ergonomics and API flexibility.

❌ BAD:
```rust
pub struct OrderProcessor {
    pub order_id: u64, pub customer_id: u64, pub items: Vec<String>,
    pub total_amount: f64, pub payment_status: String,
    pub shipping_address_street: String, pub shipping_address_city: String,
}
```

✅ GOOD:
```rust
pub struct Order {
    pub id: u64, pub customer_id: u64, pub items: Vec<String>,
    pub total_amount: f64, pub payment_status: String,
}
pub struct Address { pub street: String, pub city: String }
pub struct OrderProcessor {
    pub order: Order,
    pub shipping_address: Address,
}
```

## 2. Common Patterns and Anti-patterns

### 2.1 Newtype Pattern for Type Safety
Wrap primitive types in newtype structs to enforce strong typing and prevent logical errors from mixing incompatible IDs or values.

❌ BAD:
```rust
fn process_transaction(user_id: u64, account_id: u64) { /* ... */ }
// Calling with swapped arguments compiles: process_transaction(account.id, user.id);
```

✅ GOOD:
```rust
#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct UserId(u64);
#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct AccountId(u64);

fn process_transaction(user_id: UserId, account_id: AccountId) { /* ... */ }
// Compiler prevents: process_transaction(account.id, user.id);
```

### 2.2 Builder Pattern for Complex Construction
Use the Builder pattern for structs with many optional fields or complex initialization logic. Avoid it for simple structs with one or two clear constructors.

❌ BAD (for many fields):
```rust
pub struct Config { pub timeout: u64, pub retries: u8 }
impl Config {
    pub fn new(timeout: Option<u64>, retries: Option<u8>) -> Self { /* ... */ }
}
```

✅ GOOD:
```rust
pub struct Config { pub timeout: u64, pub retries: u8 }
impl Config { pub fn builder() -> ConfigBuilder { ConfigBuilder::default() } }

#[derive(Default)] pub struct ConfigBuilder { pub timeout: Option<u64>, pub retries: Option<u8> }
impl ConfigBuilder {
    pub fn timeout(mut self, timeout: u64) -> Self { self.timeout = Some(timeout); self }
    pub fn retries(mut self, retries: u8) -> Self { self.retries = Some(retries); self }
    pub fn build(self) -> Result<Config, &'static str> {
        Ok(Config { timeout: self.timeout.unwrap_or(1000), retries: self.retries.unwrap_or(3) })
    }
}
```

### 2.3 Minimal Generic Bounds on Types
Apply generic bounds only when they are *intrinsic* to the type definition. Place stricter, *derivable* bounds on `impl` blocks or specific functions, not on the struct/enum itself.

❌ BAD:
```rust
pub struct Cache<K: std::hash::Hash + Eq + std::fmt::Debug, V: Clone> { /* ... */ }
```

✅ GOOD:
```rust
pub struct Cache<K, V> { /* ... */ } // K and V are just types

impl<K: std::hash::Hash + Eq, V> Cache<K, V> {
    pub fn new() -> Self { /* ... */ }
}
impl<K: std::fmt::Debug, V: std::fmt::Debug> Cache<K, V> {
    pub fn print_keys(&self) { /* ... */ }
}
```

## 3. Performance Considerations

### 3.1 Default to `Vec` and `HashMap`
Start with `Vec` for sequences and `HashMap` for maps. They are highly optimized and cover most use cases. Only switch to other collections (`VecDeque`, `BTreeMap`, `LinkedList`, etc.) when profiling shows a specific performance bottleneck or a unique functional requirement (e.g., sorted keys for `BTreeMap`).

❌ BAD:
```rust
use std::collections::LinkedList; // Unnecessary for most simple lists
fn process_items(items: LinkedList<String>) { /* ... */ }
```

✅ GOOD:
```rust
use std::collections::HashMap; // Default for key-value pairs
fn process_items(items: Vec<String>) { /* ... */ }
fn lookup_data(data: HashMap<String, u32>) { /* ... */ }
```

### 3.2 Pre-allocate Collection Capacity
When the approximate size of a collection is known beforehand, pre-allocate its capacity to avoid frequent reallocations, which can be costly.

❌ BAD:
```rust
let mut vec = Vec::new();
for i in 0..1000 { vec.push(i); } // Potentially many reallocations
```

✅ GOOD:
```rust
let mut vec = Vec::with_capacity(1000);
for i in 0..1000 { vec.push(i); } // Single allocation
```

## 4. Common Pitfalls and Gotchas

### 4.1 Error Handling: `Result` for Recoverable Errors
Always use `Result<T, E>` for errors that can be handled or propagated. Reserve `panic!` for unrecoverable bugs or situations indicating a programming error.

❌ BAD:
```rust
fn load_config(path: &str) -> String {
    std::fs::read_to_string(path).expect("Failed to read config")
}
```

✅ GOOD:
```rust
fn load_config(path: &str) -> Result<String, std::io::Error> {
    std::fs::read_to_string(path)
}
```

### 4.2 Document `unsafe` Blocks
Every `unsafe` block *must* be preceded by a `// SAFETY:` comment explaining *why* the code inside is sound and upholds Rust's safety invariants.

❌ BAD:
```rust
let mut vec = vec![0];
let ptr = vec.as_mut_ptr();
unsafe { *ptr = 42; } // Missing safety justification
```

✅ GOOD:
```rust
let mut vec = vec![0];
let ptr = vec.as_mut_ptr();
// SAFETY: `ptr` is a valid pointer to `vec[0]`, and we are writing a valid
// integer `42` to it. The vector has at least one element.
unsafe { *ptr = 42; }
```

## 5. Testing Approaches

### 5.1 Unit Tests with `#[test]`
Use `#[test]` for isolated unit tests, typically placed in a `tests` module within the same file as the code being tested.

```rust
// src/lib.rs
pub fn add(a: i32, b: i32) -> i32 { a + b }

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_add() {
        assert_eq!(add(1, 2), 3);
    }
}
```

### 5.2 Example Tests with `rustdoc`
Leverage `rustdoc` for documentation examples. These examples are compiled and run as tests, ensuring your documentation stays up-to-date and correct.

```rust
/// Adds two integers together.
///
/// # Examples
///
/// ```
/// assert_eq!(my_crate::add(5, 3), 8);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Programming languages →

rust by other authors comes first.

From the same set240

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor