devflow-kit 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +69 -0
- package/README.md +35 -11
- package/dist/cli.js +5 -1
- package/dist/commands/ambient.d.ts +18 -0
- package/dist/commands/ambient.js +136 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +97 -10
- package/dist/commands/memory.d.ts +22 -0
- package/dist/commands/memory.js +175 -0
- package/dist/commands/uninstall.js +72 -5
- package/dist/plugins.js +74 -3
- package/dist/utils/post-install.d.ts +12 -0
- package/dist/utils/post-install.js +82 -1
- package/dist/utils/safe-delete-install.d.ts +7 -0
- package/dist/utils/safe-delete-install.js +40 -5
- package/package.json +2 -1
- package/plugins/devflow-accessibility/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-ambient/.claude-plugin/plugin.json +7 -0
- package/plugins/devflow-ambient/README.md +49 -0
- package/plugins/devflow-ambient/commands/ambient.md +110 -0
- package/plugins/devflow-ambient/skills/ambient-router/SKILL.md +89 -0
- package/plugins/devflow-ambient/skills/ambient-router/references/skill-catalog.md +68 -0
- package/plugins/devflow-audit-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-code-review/.claude-plugin/plugin.json +1 -4
- package/plugins/devflow-code-review/agents/reviewer.md +8 -0
- package/plugins/devflow-code-review/commands/code-review-teams.md +11 -1
- package/plugins/devflow-code-review/commands/code-review.md +12 -2
- package/plugins/devflow-core-skills/.claude-plugin/plugin.json +3 -6
- package/plugins/devflow-core-skills/skills/docs-framework/SKILL.md +10 -6
- package/plugins/devflow-core-skills/skills/test-driven-development/SKILL.md +139 -0
- package/plugins/devflow-core-skills/skills/test-driven-development/references/rationalization-prevention.md +111 -0
- package/plugins/devflow-debug/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-frontend-design/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-go/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-go/skills/go/SKILL.md +187 -0
- package/plugins/devflow-go/skills/go/references/concurrency.md +312 -0
- package/plugins/devflow-go/skills/go/references/detection.md +129 -0
- package/plugins/devflow-go/skills/go/references/patterns.md +232 -0
- package/plugins/devflow-go/skills/go/references/violations.md +205 -0
- package/plugins/devflow-implement/.claude-plugin/plugin.json +1 -3
- package/plugins/devflow-implement/agents/coder.md +11 -6
- package/plugins/devflow-java/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-java/skills/java/SKILL.md +183 -0
- package/plugins/devflow-java/skills/java/references/detection.md +120 -0
- package/plugins/devflow-java/skills/java/references/modern-java.md +270 -0
- package/plugins/devflow-java/skills/java/references/patterns.md +235 -0
- package/plugins/devflow-java/skills/java/references/violations.md +213 -0
- package/plugins/devflow-python/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-python/skills/python/SKILL.md +188 -0
- package/plugins/devflow-python/skills/python/references/async.md +220 -0
- package/plugins/devflow-python/skills/python/references/detection.md +128 -0
- package/plugins/devflow-python/skills/python/references/patterns.md +226 -0
- package/plugins/devflow-python/skills/python/references/violations.md +204 -0
- package/plugins/devflow-react/.claude-plugin/plugin.json +15 -0
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/SKILL.md +1 -1
- package/plugins/{devflow-core-skills → devflow-react}/skills/react/references/patterns.md +3 -3
- package/plugins/devflow-resolve/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-rust/.claude-plugin/plugin.json +15 -0
- package/plugins/devflow-rust/skills/rust/SKILL.md +193 -0
- package/plugins/devflow-rust/skills/rust/references/detection.md +131 -0
- package/plugins/devflow-rust/skills/rust/references/ownership.md +242 -0
- package/plugins/devflow-rust/skills/rust/references/patterns.md +210 -0
- package/plugins/devflow-rust/skills/rust/references/violations.md +191 -0
- package/plugins/devflow-self-review/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-specify/.claude-plugin/plugin.json +1 -1
- package/plugins/devflow-typescript/.claude-plugin/plugin.json +15 -0
- package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/patterns.md +3 -3
- package/scripts/hooks/ambient-prompt.sh +48 -0
- package/scripts/hooks/background-memory-update.sh +49 -8
- package/scripts/hooks/ensure-memory-gitignore.sh +17 -0
- package/scripts/hooks/pre-compact-memory.sh +12 -6
- package/scripts/hooks/session-start-memory.sh +50 -8
- package/scripts/hooks/stop-update-memory.sh +10 -6
- package/shared/agents/coder.md +11 -6
- package/shared/agents/reviewer.md +8 -0
- package/shared/skills/ambient-router/SKILL.md +89 -0
- package/shared/skills/ambient-router/references/skill-catalog.md +68 -0
- package/shared/skills/docs-framework/SKILL.md +10 -6
- package/shared/skills/go/SKILL.md +187 -0
- package/shared/skills/go/references/concurrency.md +312 -0
- package/shared/skills/go/references/detection.md +129 -0
- package/shared/skills/go/references/patterns.md +232 -0
- package/shared/skills/go/references/violations.md +205 -0
- package/shared/skills/java/SKILL.md +183 -0
- package/shared/skills/java/references/detection.md +120 -0
- package/shared/skills/java/references/modern-java.md +270 -0
- package/shared/skills/java/references/patterns.md +235 -0
- package/shared/skills/java/references/violations.md +213 -0
- package/shared/skills/python/SKILL.md +188 -0
- package/shared/skills/python/references/async.md +220 -0
- package/shared/skills/python/references/detection.md +128 -0
- package/shared/skills/python/references/patterns.md +226 -0
- package/shared/skills/python/references/violations.md +204 -0
- package/shared/skills/react/SKILL.md +1 -1
- package/shared/skills/react/references/patterns.md +3 -3
- package/shared/skills/rust/SKILL.md +193 -0
- package/shared/skills/rust/references/detection.md +131 -0
- package/shared/skills/rust/references/ownership.md +242 -0
- package/shared/skills/rust/references/patterns.md +210 -0
- package/shared/skills/rust/references/violations.md +191 -0
- package/shared/skills/test-driven-development/SKILL.md +139 -0
- package/shared/skills/test-driven-development/references/rationalization-prevention.md +111 -0
- package/shared/skills/typescript/references/patterns.md +3 -3
- package/src/templates/managed-settings.json +14 -0
- package/plugins/devflow-code-review/skills/react/SKILL.md +0 -276
- package/plugins/devflow-code-review/skills/react/references/patterns.md +0 -1331
- package/plugins/devflow-core-skills/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-core-skills/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-core-skills/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-core-skills/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-core-skills/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-core-skills/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-core-skills/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-core-skills/skills/frontend-design/references/violations.md +0 -453
- package/plugins/devflow-core-skills/skills/react/references/violations.md +0 -565
- package/plugins/devflow-implement/skills/accessibility/SKILL.md +0 -229
- package/plugins/devflow-implement/skills/accessibility/references/detection.md +0 -171
- package/plugins/devflow-implement/skills/accessibility/references/patterns.md +0 -670
- package/plugins/devflow-implement/skills/accessibility/references/violations.md +0 -419
- package/plugins/devflow-implement/skills/frontend-design/SKILL.md +0 -254
- package/plugins/devflow-implement/skills/frontend-design/references/detection.md +0 -184
- package/plugins/devflow-implement/skills/frontend-design/references/patterns.md +0 -511
- package/plugins/devflow-implement/skills/frontend-design/references/violations.md +0 -453
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-accessibility}/skills/accessibility/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/SKILL.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/detection.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/patterns.md +0 -0
- /package/plugins/{devflow-code-review → devflow-frontend-design}/skills/frontend-design/references/violations.md +0 -0
- /package/plugins/{devflow-code-review → devflow-react}/skills/react/references/violations.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/SKILL.md +0 -0
- /package/plugins/{devflow-core-skills → devflow-typescript}/skills/typescript/references/violations.md +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rust
|
|
3
|
+
description: This skill should be used when the user works with Rust files (.rs), asks about "ownership", "borrowing", "lifetimes", "Result/Option", "traits", or discusses memory safety and type-driven design. Provides patterns for ownership, error handling, type system usage, and safe concurrency.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
allowed-tools: Read, Grep, Glob
|
|
6
|
+
activation:
|
|
7
|
+
file-patterns:
|
|
8
|
+
- "**/*.rs"
|
|
9
|
+
exclude:
|
|
10
|
+
- "**/target/**"
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Rust Patterns
|
|
14
|
+
|
|
15
|
+
Reference for Rust-specific patterns, ownership model, and type-driven design.
|
|
16
|
+
|
|
17
|
+
## Iron Law
|
|
18
|
+
|
|
19
|
+
> **MAKE ILLEGAL STATES UNREPRESENTABLE**
|
|
20
|
+
>
|
|
21
|
+
> Encode invariants in the type system. If a function can fail, return `Result`. If a value
|
|
22
|
+
> might be absent, return `Option`. If a state transition is invalid, make it uncompilable.
|
|
23
|
+
> Runtime checks are a fallback, not a strategy.
|
|
24
|
+
|
|
25
|
+
## When This Skill Activates
|
|
26
|
+
|
|
27
|
+
- Working with Rust codebases
|
|
28
|
+
- Designing type-safe APIs
|
|
29
|
+
- Managing ownership and borrowing
|
|
30
|
+
- Implementing error handling
|
|
31
|
+
- Writing concurrent code
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Ownership & Borrowing
|
|
36
|
+
|
|
37
|
+
### Prefer Borrowing Over Cloning
|
|
38
|
+
|
|
39
|
+
```rust
|
|
40
|
+
// BAD: fn process(data: String) — takes ownership unnecessarily
|
|
41
|
+
// GOOD: fn process(data: &str) — borrows, caller keeps ownership
|
|
42
|
+
|
|
43
|
+
fn process(data: &str) -> usize {
|
|
44
|
+
data.len()
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Lifetime Annotations When Needed
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
// Return reference tied to input lifetime
|
|
52
|
+
fn first_word(s: &str) -> &str {
|
|
53
|
+
s.split_whitespace().next().unwrap_or("")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Explicit when compiler can't infer
|
|
57
|
+
struct Excerpt<'a> {
|
|
58
|
+
text: &'a str,
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Error Handling
|
|
65
|
+
|
|
66
|
+
### Use Result and the ? Operator
|
|
67
|
+
|
|
68
|
+
```rust
|
|
69
|
+
use std::fs;
|
|
70
|
+
use std::io;
|
|
71
|
+
|
|
72
|
+
fn read_config(path: &str) -> Result<Config, AppError> {
|
|
73
|
+
let content = fs::read_to_string(path)
|
|
74
|
+
.map_err(|e| AppError::Io { path: path.into(), source: e })?;
|
|
75
|
+
let config: Config = toml::from_str(&content)
|
|
76
|
+
.map_err(|e| AppError::Parse { source: e })?;
|
|
77
|
+
Ok(config)
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Custom Error Types with thiserror
|
|
82
|
+
|
|
83
|
+
```rust
|
|
84
|
+
use thiserror::Error;
|
|
85
|
+
|
|
86
|
+
#[derive(Error, Debug)]
|
|
87
|
+
pub enum AppError {
|
|
88
|
+
#[error("IO error reading {path}")]
|
|
89
|
+
Io { path: String, #[source] source: io::Error },
|
|
90
|
+
#[error("parse error")]
|
|
91
|
+
Parse { #[from] source: toml::de::Error },
|
|
92
|
+
#[error("{entity} with id {id} not found")]
|
|
93
|
+
NotFound { entity: String, id: String },
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Type System
|
|
100
|
+
|
|
101
|
+
### Newtype Pattern
|
|
102
|
+
|
|
103
|
+
```rust
|
|
104
|
+
// Prevent mixing up IDs
|
|
105
|
+
struct UserId(String);
|
|
106
|
+
struct OrderId(String);
|
|
107
|
+
|
|
108
|
+
fn get_order(user_id: &UserId, order_id: &OrderId) -> Result<Order, AppError> {
|
|
109
|
+
// Can't accidentally swap parameters
|
|
110
|
+
todo!()
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Enums for State Machines
|
|
115
|
+
|
|
116
|
+
```rust
|
|
117
|
+
enum Connection {
|
|
118
|
+
Disconnected,
|
|
119
|
+
Connecting { attempt: u32 },
|
|
120
|
+
Connected { session: Session },
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Each state carries only its relevant data
|
|
124
|
+
// Invalid transitions are uncompilable
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Patterns
|
|
130
|
+
|
|
131
|
+
### Builder Pattern
|
|
132
|
+
|
|
133
|
+
```rust
|
|
134
|
+
pub struct ServerBuilder {
|
|
135
|
+
port: u16,
|
|
136
|
+
host: String,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
impl ServerBuilder {
|
|
140
|
+
pub fn new() -> Self { Self { port: 8080, host: "localhost".into() } }
|
|
141
|
+
pub fn port(mut self, port: u16) -> Self { self.port = port; self }
|
|
142
|
+
pub fn host(mut self, host: impl Into<String>) -> Self { self.host = host.into(); self }
|
|
143
|
+
pub fn build(self) -> Server { Server { port: self.port, host: self.host } }
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Iterator Chains Over Loops
|
|
148
|
+
|
|
149
|
+
```rust
|
|
150
|
+
// BAD: manual loop with push
|
|
151
|
+
// GOOD:
|
|
152
|
+
let active_names: Vec<&str> = users.iter()
|
|
153
|
+
.filter(|u| u.is_active)
|
|
154
|
+
.map(|u| u.name.as_str())
|
|
155
|
+
.collect();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Anti-Patterns
|
|
161
|
+
|
|
162
|
+
| Pattern | Bad | Good |
|
|
163
|
+
|---------|-----|------|
|
|
164
|
+
| Unwrap in library | `.unwrap()` | `?` operator or `.ok_or()` |
|
|
165
|
+
| Clone to satisfy borrow checker | `.clone()` everywhere | Restructure ownership |
|
|
166
|
+
| String for everything | `HashMap<String, String>` | Typed structs and enums |
|
|
167
|
+
| Ignoring Result | `let _ = write(...)` | Handle or propagate error |
|
|
168
|
+
| Mutex<Vec> for message passing | Shared mutable state | Channels (`mpsc`) |
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Extended References
|
|
173
|
+
|
|
174
|
+
For additional patterns and examples:
|
|
175
|
+
- `references/violations.md` - Common Rust violations
|
|
176
|
+
- `references/patterns.md` - Extended Rust patterns
|
|
177
|
+
- `references/detection.md` - Detection patterns for Rust issues
|
|
178
|
+
- `references/ownership.md` - Advanced ownership and lifetime patterns
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Checklist
|
|
183
|
+
|
|
184
|
+
- [ ] No `.unwrap()` in library/application code (ok in tests)
|
|
185
|
+
- [ ] Custom error types with `thiserror`
|
|
186
|
+
- [ ] `?` operator for error propagation
|
|
187
|
+
- [ ] Borrow instead of clone where possible
|
|
188
|
+
- [ ] Newtype pattern for type-safe IDs
|
|
189
|
+
- [ ] Enums for state machines
|
|
190
|
+
- [ ] Iterator chains over manual loops
|
|
191
|
+
- [ ] `#[must_use]` on Result-returning functions
|
|
192
|
+
- [ ] No `unsafe` without safety comment
|
|
193
|
+
- [ ] Clippy clean (`cargo clippy -- -D warnings`)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Rust Detection Patterns
|
|
2
|
+
|
|
3
|
+
Grep and regex patterns for finding common Rust issues. Use with `Grep` tool.
|
|
4
|
+
|
|
5
|
+
## Unwrap and Expect
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Find .unwrap() calls (exclude tests)
|
|
9
|
+
grep -rn '\.unwrap()' --include='*.rs' --exclude-dir=tests --exclude='*_test.rs'
|
|
10
|
+
|
|
11
|
+
# Find .expect() without descriptive message
|
|
12
|
+
grep -rn '\.expect("")' --include='*.rs'
|
|
13
|
+
|
|
14
|
+
# Find unwrap_or_default hiding errors
|
|
15
|
+
grep -rn '\.unwrap_or_default()' --include='*.rs'
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**Pattern**: `\.unwrap\(\)` — matches any `.unwrap()` call
|
|
19
|
+
**Pattern**: `\.expect\("` — matches `.expect("` to review message quality
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Clone Abuse
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Find .clone() calls — review each for necessity
|
|
27
|
+
grep -rn '\.clone()' --include='*.rs'
|
|
28
|
+
|
|
29
|
+
# Find clone in loop bodies (likely hot-path waste)
|
|
30
|
+
grep -rn -A2 'for.*in' --include='*.rs' | grep '\.clone()'
|
|
31
|
+
|
|
32
|
+
# Find to_string() where &str would work
|
|
33
|
+
grep -rn '\.to_string()' --include='*.rs'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Pattern**: `\.clone\(\)` — all clone calls for manual review
|
|
37
|
+
**Pattern**: `\.to_owned\(\)` — ownership transfer that may be unnecessary
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Unsafe Blocks
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Find all unsafe blocks
|
|
45
|
+
grep -rn 'unsafe\s*{' --include='*.rs'
|
|
46
|
+
|
|
47
|
+
# Find unsafe without SAFETY comment
|
|
48
|
+
grep -rn -B2 'unsafe\s*{' --include='*.rs' | grep -v 'SAFETY'
|
|
49
|
+
|
|
50
|
+
# Find unsafe functions
|
|
51
|
+
grep -rn 'unsafe fn' --include='*.rs'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Pattern**: `unsafe\s*\{` — unsafe blocks
|
|
55
|
+
**Pattern**: `unsafe fn` — unsafe function declarations
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Incomplete Code
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Find todo! and unimplemented! macros
|
|
63
|
+
grep -rn 'todo!\|unimplemented!' --include='*.rs'
|
|
64
|
+
|
|
65
|
+
# Find unreachable! that may hide bugs
|
|
66
|
+
grep -rn 'unreachable!' --include='*.rs'
|
|
67
|
+
|
|
68
|
+
# Find panic! in non-test code
|
|
69
|
+
grep -rn 'panic!' --include='*.rs' --exclude-dir=tests --exclude='*_test.rs'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Pattern**: `todo!\(\)` — placeholder code
|
|
73
|
+
**Pattern**: `unimplemented!\(\)` — unfinished implementations
|
|
74
|
+
**Pattern**: `panic!\(` — explicit panics outside tests
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Error Handling Issues
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Find ignored Results (let _ = expr that returns Result)
|
|
82
|
+
grep -rn 'let _ =' --include='*.rs'
|
|
83
|
+
|
|
84
|
+
# Find empty match arms that may swallow errors
|
|
85
|
+
grep -rn '=> {}' --include='*.rs'
|
|
86
|
+
|
|
87
|
+
# Find catch-all match arms hiding missing cases
|
|
88
|
+
grep -rn '_ =>' --include='*.rs'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Pattern**: `let _ =` — potentially ignored Result or important value
|
|
92
|
+
**Pattern**: `=> \{\}` — empty match arm (may swallow error)
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Concurrency Red Flags
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Find static mut (almost always wrong)
|
|
100
|
+
grep -rn 'static mut' --include='*.rs'
|
|
101
|
+
|
|
102
|
+
# Find blocking calls in async functions
|
|
103
|
+
grep -rn 'std::fs::' --include='*.rs' | grep -v 'test'
|
|
104
|
+
grep -rn 'std::thread::sleep' --include='*.rs'
|
|
105
|
+
|
|
106
|
+
# Find Mutex without Arc in multi-threaded context
|
|
107
|
+
grep -rn 'Mutex::new' --include='*.rs'
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Pattern**: `static mut` — mutable global state (data race risk)
|
|
111
|
+
**Pattern**: `std::fs::` — blocking I/O that may appear in async context
|
|
112
|
+
**Pattern**: `std::thread::sleep` — blocking sleep (use `tokio::time::sleep` in async)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Clippy Lints
|
|
117
|
+
|
|
118
|
+
Run Clippy for automated detection of many patterns above:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
cargo clippy -- -D warnings
|
|
122
|
+
cargo clippy -- -W clippy::pedantic
|
|
123
|
+
cargo clippy -- -W clippy::nursery
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Key Clippy lints that catch issues:
|
|
127
|
+
- `clippy::unwrap_used` — flags unwrap calls
|
|
128
|
+
- `clippy::clone_on_ref_ptr` — unnecessary Arc/Rc clone
|
|
129
|
+
- `clippy::needless_pass_by_value` — should borrow instead
|
|
130
|
+
- `clippy::missing_errors_doc` — public Result fn without doc
|
|
131
|
+
- `clippy::wildcard_enum_match_arm` — catch-all hiding cases
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# Rust Ownership Deep Dive
|
|
2
|
+
|
|
3
|
+
Advanced ownership patterns, lifetime elision, interior mutability, and pinning.
|
|
4
|
+
|
|
5
|
+
## Lifetime Elision Rules
|
|
6
|
+
|
|
7
|
+
The compiler applies three rules to infer lifetimes. When they don't resolve, annotate manually.
|
|
8
|
+
|
|
9
|
+
### Rule 1: Each Reference Parameter Gets Its Own Lifetime
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Compiler sees: fn first(s: &str) -> &str
|
|
13
|
+
// Compiler infers: fn first<'a>(s: &'a str) -> &'a str
|
|
14
|
+
fn first(s: &str) -> &str {
|
|
15
|
+
&s[..1]
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Rule 2: Single Input Lifetime Applies to All Outputs
|
|
20
|
+
|
|
21
|
+
```rust
|
|
22
|
+
// One input reference — output borrows from it
|
|
23
|
+
fn trim(s: &str) -> &str {
|
|
24
|
+
s.trim()
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Rule 3: &self Lifetime Applies to All Outputs in Methods
|
|
29
|
+
|
|
30
|
+
```rust
|
|
31
|
+
impl Config {
|
|
32
|
+
// &self lifetime flows to return
|
|
33
|
+
fn database_url(&self) -> &str {
|
|
34
|
+
&self.db_url
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### When Elision Fails
|
|
40
|
+
|
|
41
|
+
```rust
|
|
42
|
+
// Two input lifetimes — compiler can't decide which output borrows from
|
|
43
|
+
// Must annotate: output borrows from `a`, not `b`
|
|
44
|
+
fn longest<'a>(a: &'a str, b: &str) -> &'a str {
|
|
45
|
+
if a.len() >= b.len() { a } else { a }
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Interior Mutability
|
|
52
|
+
|
|
53
|
+
Mutate data behind a shared reference when ownership rules are too strict.
|
|
54
|
+
|
|
55
|
+
### Cell — Copy Types Only
|
|
56
|
+
|
|
57
|
+
```rust
|
|
58
|
+
use std::cell::Cell;
|
|
59
|
+
|
|
60
|
+
struct Counter {
|
|
61
|
+
count: Cell<u32>, // Mutate through &self
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
impl Counter {
|
|
65
|
+
fn increment(&self) {
|
|
66
|
+
self.count.set(self.count.get() + 1);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### RefCell — Runtime Borrow Checking
|
|
72
|
+
|
|
73
|
+
```rust
|
|
74
|
+
use std::cell::RefCell;
|
|
75
|
+
|
|
76
|
+
struct Cache {
|
|
77
|
+
data: RefCell<HashMap<String, String>>,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
impl Cache {
|
|
81
|
+
fn get_or_insert(&self, key: &str, value: &str) -> String {
|
|
82
|
+
let mut data = self.data.borrow_mut(); // Panics if already borrowed
|
|
83
|
+
data.entry(key.to_string())
|
|
84
|
+
.or_insert_with(|| value.to_string())
|
|
85
|
+
.clone()
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Mutex — Thread-Safe Interior Mutability
|
|
91
|
+
|
|
92
|
+
```rust
|
|
93
|
+
use std::sync::Mutex;
|
|
94
|
+
|
|
95
|
+
struct SharedState {
|
|
96
|
+
data: Mutex<Vec<String>>,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
impl SharedState {
|
|
100
|
+
fn push(&self, item: String) -> Result<(), AppError> {
|
|
101
|
+
let mut data = self.data.lock()
|
|
102
|
+
.map_err(|_| AppError::LockPoisoned)?;
|
|
103
|
+
data.push(item);
|
|
104
|
+
Ok(())
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Decision Guide
|
|
110
|
+
|
|
111
|
+
| Type | Thread-Safe | Cost | Use Case |
|
|
112
|
+
|------|-------------|------|----------|
|
|
113
|
+
| `Cell<T>` | No | Zero | Copy types, single-threaded |
|
|
114
|
+
| `RefCell<T>` | No | Runtime borrow check | Non-Copy, single-threaded |
|
|
115
|
+
| `Mutex<T>` | Yes | Lock overhead | Multi-threaded mutation |
|
|
116
|
+
| `RwLock<T>` | Yes | Lock overhead | Multi-threaded, read-heavy |
|
|
117
|
+
| `Atomic*` | Yes | Hardware atomic | Counters, flags |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Cow — Clone on Write
|
|
122
|
+
|
|
123
|
+
Defer cloning until mutation is actually needed.
|
|
124
|
+
|
|
125
|
+
```rust
|
|
126
|
+
use std::borrow::Cow;
|
|
127
|
+
|
|
128
|
+
// Returns borrowed if no processing needed, owned if modified
|
|
129
|
+
fn normalize_path(path: &str) -> Cow<'_, str> {
|
|
130
|
+
if path.contains("//") {
|
|
131
|
+
Cow::Owned(path.replace("//", "/"))
|
|
132
|
+
} else {
|
|
133
|
+
Cow::Borrowed(path)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Function accepts both owned and borrowed transparently
|
|
138
|
+
fn process(input: Cow<'_, str>) {
|
|
139
|
+
println!("{}", input); // No allocation if already borrowed
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Cow in APIs
|
|
144
|
+
|
|
145
|
+
```rust
|
|
146
|
+
// Accept Cow for flexible ownership — caller decides allocation
|
|
147
|
+
pub fn log_message(msg: Cow<'_, str>) {
|
|
148
|
+
eprintln!("[LOG] {}", msg);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Caller with borrowed data — zero-copy
|
|
152
|
+
log_message(Cow::Borrowed("static message"));
|
|
153
|
+
|
|
154
|
+
// Caller with owned data — no extra clone
|
|
155
|
+
log_message(Cow::Owned(format!("dynamic: {}", value)));
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Pin for Async and Self-Referential Types
|
|
161
|
+
|
|
162
|
+
### Why Pin Exists
|
|
163
|
+
|
|
164
|
+
Self-referential structs break if moved in memory. `Pin` guarantees the value won't move.
|
|
165
|
+
|
|
166
|
+
```rust
|
|
167
|
+
use std::pin::Pin;
|
|
168
|
+
use std::future::Future;
|
|
169
|
+
|
|
170
|
+
// Async functions return self-referential futures
|
|
171
|
+
// Pin ensures the future stays in place while polled
|
|
172
|
+
fn fetch_data(url: &str) -> Pin<Box<dyn Future<Output = Result<Data, Error>> + '_>> {
|
|
173
|
+
Box::pin(async move {
|
|
174
|
+
let response = reqwest::get(url).await?;
|
|
175
|
+
let data = response.json::<Data>().await?;
|
|
176
|
+
Ok(data)
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Pin in Practice
|
|
182
|
+
|
|
183
|
+
```rust
|
|
184
|
+
use tokio::pin;
|
|
185
|
+
|
|
186
|
+
async fn process_stream(stream: impl Stream<Item = Data>) {
|
|
187
|
+
// pin! macro pins the stream to the stack
|
|
188
|
+
pin!(stream);
|
|
189
|
+
|
|
190
|
+
while let Some(item) = stream.next().await {
|
|
191
|
+
handle(item).await;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### When You Need Pin
|
|
197
|
+
|
|
198
|
+
| Scenario | Need Pin? |
|
|
199
|
+
|----------|-----------|
|
|
200
|
+
| Returning `async` blocks as trait objects | Yes |
|
|
201
|
+
| Implementing `Future` manually | Yes |
|
|
202
|
+
| Using `tokio::select!` on futures | Yes (automatically handled) |
|
|
203
|
+
| Normal async/await | No (compiler handles it) |
|
|
204
|
+
| Storing futures in collections | Yes (`Pin<Box<dyn Future>>`) |
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Ownership Transfer Patterns
|
|
209
|
+
|
|
210
|
+
### Take Pattern — Move Out of Option
|
|
211
|
+
|
|
212
|
+
```rust
|
|
213
|
+
struct Connection {
|
|
214
|
+
session: Option<Session>,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
impl Connection {
|
|
218
|
+
fn close(&mut self) -> Option<Session> {
|
|
219
|
+
self.session.take() // Moves out, leaves None
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Swap Pattern — Replace In Place
|
|
225
|
+
|
|
226
|
+
```rust
|
|
227
|
+
use std::mem;
|
|
228
|
+
|
|
229
|
+
fn rotate_buffer(current: &mut Vec<u8>, new_data: Vec<u8>) -> Vec<u8> {
|
|
230
|
+
mem::replace(current, new_data) // Returns old, installs new
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Entry Pattern — Conditional Insertion
|
|
235
|
+
|
|
236
|
+
```rust
|
|
237
|
+
use std::collections::HashMap;
|
|
238
|
+
|
|
239
|
+
fn get_or_create(map: &mut HashMap<String, Vec<Item>>, key: &str) -> &mut Vec<Item> {
|
|
240
|
+
map.entry(key.to_string()).or_insert_with(Vec::new)
|
|
241
|
+
}
|
|
242
|
+
```
|