tribunal-kit 2.4.6 → 3.1.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/.agent/ARCHITECTURE.md +99 -99
- package/.agent/GEMINI.md +52 -52
- package/.agent/agents/accessibility-reviewer.md +139 -86
- package/.agent/agents/ai-code-reviewer.md +160 -90
- package/.agent/agents/backend-specialist.md +164 -127
- package/.agent/agents/code-archaeologist.md +115 -73
- package/.agent/agents/database-architect.md +130 -110
- package/.agent/agents/debugger.md +137 -97
- package/.agent/agents/dependency-reviewer.md +78 -30
- package/.agent/agents/devops-engineer.md +161 -118
- package/.agent/agents/documentation-writer.md +151 -87
- package/.agent/agents/explorer-agent.md +117 -99
- package/.agent/agents/frontend-reviewer.md +127 -47
- package/.agent/agents/frontend-specialist.md +169 -109
- package/.agent/agents/game-developer.md +28 -164
- package/.agent/agents/logic-reviewer.md +87 -49
- package/.agent/agents/mobile-developer.md +151 -103
- package/.agent/agents/mobile-reviewer.md +133 -50
- package/.agent/agents/orchestrator.md +121 -110
- package/.agent/agents/penetration-tester.md +103 -77
- package/.agent/agents/performance-optimizer.md +136 -92
- package/.agent/agents/performance-reviewer.md +139 -69
- package/.agent/agents/product-manager.md +104 -70
- package/.agent/agents/product-owner.md +6 -25
- package/.agent/agents/project-planner.md +95 -95
- package/.agent/agents/qa-automation-engineer.md +174 -87
- package/.agent/agents/security-auditor.md +133 -129
- package/.agent/agents/seo-specialist.md +160 -99
- package/.agent/agents/sql-reviewer.md +132 -44
- package/.agent/agents/supervisor-agent.md +137 -109
- package/.agent/agents/swarm-worker-contracts.md +17 -17
- package/.agent/agents/swarm-worker-registry.md +46 -46
- package/.agent/agents/test-coverage-reviewer.md +132 -53
- package/.agent/agents/test-engineer.md +0 -21
- package/.agent/agents/type-safety-reviewer.md +143 -33
- package/.agent/patterns/generator.md +9 -9
- package/.agent/patterns/inversion.md +12 -12
- package/.agent/patterns/pipeline.md +9 -9
- package/.agent/patterns/reviewer.md +13 -13
- package/.agent/patterns/tool-wrapper.md +9 -9
- package/.agent/rules/GEMINI.md +63 -63
- package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
- package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
- package/.agent/scripts/compress_skills.py +167 -0
- package/.agent/scripts/consolidate_skills.py +173 -0
- package/.agent/scripts/deep_compress.py +202 -0
- package/.agent/scripts/minify_context.py +80 -0
- package/.agent/scripts/security_scan.py +1 -1
- package/.agent/scripts/strip_tribunal.py +41 -0
- package/.agent/skills/agent-organizer/SKILL.md +60 -100
- package/.agent/skills/agentic-patterns/SKILL.md +0 -70
- package/.agent/skills/ai-prompt-injection-defense/SKILL.md +108 -53
- package/.agent/skills/api-patterns/SKILL.md +197 -257
- package/.agent/skills/api-security-auditor/SKILL.md +125 -57
- package/.agent/skills/app-builder/SKILL.md +326 -50
- package/.agent/skills/app-builder/templates/SKILL.md +13 -15
- package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +16 -16
- package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +22 -22
- package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +20 -20
- package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +17 -17
- package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +21 -21
- package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +19 -19
- package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +26 -26
- package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +26 -26
- package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +19 -19
- package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +18 -18
- package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +20 -20
- package/.agent/skills/appflow-wireframe/SKILL.md +71 -98
- package/.agent/skills/architecture/SKILL.md +161 -200
- package/.agent/skills/authentication-best-practices/SKILL.md +121 -54
- package/.agent/skills/bash-linux/SKILL.md +71 -166
- package/.agent/skills/behavioral-modes/SKILL.md +8 -69
- package/.agent/skills/brainstorming/SKILL.md +345 -127
- package/.agent/skills/building-native-ui/SKILL.md +125 -57
- package/.agent/skills/clean-code/SKILL.md +266 -149
- package/.agent/skills/code-review-checklist/SKILL.md +0 -62
- package/.agent/skills/config-validator/SKILL.md +73 -131
- package/.agent/skills/csharp-developer/SKILL.md +434 -73
- package/.agent/skills/database-design/SKILL.md +190 -275
- package/.agent/skills/deployment-procedures/SKILL.md +81 -158
- package/.agent/skills/devops-engineer/SKILL.md +255 -94
- package/.agent/skills/devops-incident-responder/SKILL.md +50 -69
- package/.agent/skills/doc.md +5 -5
- package/.agent/skills/documentation-templates/SKILL.md +19 -63
- package/.agent/skills/edge-computing/SKILL.md +75 -165
- package/.agent/skills/extract-design-system/SKILL.md +84 -58
- package/.agent/skills/framer-motion-expert/SKILL.md +195 -0
- package/.agent/skills/frontend-design/SKILL.md +151 -499
- package/.agent/skills/game-design-expert/SKILL.md +71 -0
- package/.agent/skills/game-engineering-expert/SKILL.md +88 -0
- package/.agent/skills/geo-fundamentals/SKILL.md +52 -178
- package/.agent/skills/github-operations/SKILL.md +197 -272
- package/.agent/skills/gsap-expert/SKILL.md +194 -0
- package/.agent/skills/i18n-localization/SKILL.md +60 -172
- package/.agent/skills/intelligent-routing/SKILL.md +123 -103
- package/.agent/skills/lint-and-validate/SKILL.md +8 -52
- package/.agent/skills/llm-engineering/SKILL.md +281 -195
- package/.agent/skills/local-first/SKILL.md +76 -159
- package/.agent/skills/mcp-builder/SKILL.md +48 -188
- package/.agent/skills/mobile-design/SKILL.md +213 -219
- package/.agent/skills/motion-engineering/SKILL.md +184 -0
- package/.agent/skills/nextjs-react-expert/SKILL.md +184 -203
- package/.agent/skills/nodejs-best-practices/SKILL.md +403 -185
- package/.agent/skills/observability/SKILL.md +211 -203
- package/.agent/skills/parallel-agents/SKILL.md +53 -146
- package/.agent/skills/performance-profiling/SKILL.md +171 -151
- package/.agent/skills/plan-writing/SKILL.md +49 -153
- package/.agent/skills/platform-engineer/SKILL.md +57 -103
- package/.agent/skills/playwright-best-practices/SKILL.md +110 -63
- package/.agent/skills/powershell-windows/SKILL.md +61 -179
- package/.agent/skills/python-patterns/SKILL.md +7 -35
- package/.agent/skills/python-pro/SKILL.md +273 -114
- package/.agent/skills/react-specialist/SKILL.md +227 -108
- package/.agent/skills/readme-builder/SKILL.md +15 -85
- package/.agent/skills/realtime-patterns/SKILL.md +216 -243
- package/.agent/skills/red-team-tactics/SKILL.md +10 -51
- package/.agent/skills/rust-pro/SKILL.md +525 -142
- package/.agent/skills/seo-fundamentals/SKILL.md +92 -153
- package/.agent/skills/server-management/SKILL.md +110 -166
- package/.agent/skills/shadcn-ui-expert/SKILL.md +154 -55
- package/.agent/skills/skill-creator/SKILL.md +18 -58
- package/.agent/skills/sql-pro/SKILL.md +543 -68
- package/.agent/skills/supabase-postgres-best-practices/SKILL.md +28 -68
- package/.agent/skills/swiftui-expert/SKILL.md +124 -57
- package/.agent/skills/systematic-debugging/SKILL.md +49 -151
- package/.agent/skills/tailwind-patterns/SKILL.md +433 -149
- package/.agent/skills/tdd-workflow/SKILL.md +63 -169
- package/.agent/skills/test-result-analyzer/SKILL.md +33 -73
- package/.agent/skills/testing-patterns/SKILL.md +437 -130
- package/.agent/skills/trend-researcher/SKILL.md +30 -71
- package/.agent/skills/ui-ux-pro-max/SKILL.md +0 -41
- package/.agent/skills/ui-ux-researcher/SKILL.md +51 -91
- package/.agent/skills/vue-expert/SKILL.md +225 -119
- package/.agent/skills/vulnerability-scanner/SKILL.md +264 -226
- package/.agent/skills/web-accessibility-auditor/SKILL.md +141 -58
- package/.agent/skills/web-design-guidelines/SKILL.md +17 -61
- package/.agent/skills/webapp-testing/SKILL.md +71 -196
- package/.agent/skills/whimsy-injector/SKILL.md +58 -132
- package/.agent/skills/workflow-optimizer/SKILL.md +28 -68
- package/.agent/workflows/api-tester.md +96 -224
- package/.agent/workflows/audit.md +81 -122
- package/.agent/workflows/brainstorm.md +69 -105
- package/.agent/workflows/changelog.md +65 -97
- package/.agent/workflows/create.md +73 -88
- package/.agent/workflows/debug.md +80 -111
- package/.agent/workflows/deploy.md +119 -92
- package/.agent/workflows/enhance.md +80 -91
- package/.agent/workflows/fix.md +68 -97
- package/.agent/workflows/generate.md +165 -164
- package/.agent/workflows/migrate.md +106 -109
- package/.agent/workflows/orchestrate.md +103 -86
- package/.agent/workflows/performance-benchmarker.md +77 -268
- package/.agent/workflows/plan.md +120 -98
- package/.agent/workflows/preview.md +39 -96
- package/.agent/workflows/refactor.md +105 -97
- package/.agent/workflows/review-ai.md +63 -102
- package/.agent/workflows/review.md +71 -110
- package/.agent/workflows/session.md +53 -113
- package/.agent/workflows/status.md +42 -88
- package/.agent/workflows/strengthen-skills.md +90 -51
- package/.agent/workflows/swarm.md +114 -129
- package/.agent/workflows/test.md +125 -102
- package/.agent/workflows/tribunal-backend.md +60 -78
- package/.agent/workflows/tribunal-database.md +62 -100
- package/.agent/workflows/tribunal-frontend.md +62 -82
- package/.agent/workflows/tribunal-full.md +56 -100
- package/.agent/workflows/tribunal-mobile.md +65 -94
- package/.agent/workflows/tribunal-performance.md +62 -105
- package/.agent/workflows/ui-ux-pro-max.md +72 -121
- package/README.md +11 -15
- package/package.json +1 -1
- package/.agent/skills/api-patterns/api-style.md +0 -42
- package/.agent/skills/api-patterns/auth.md +0 -24
- package/.agent/skills/api-patterns/documentation.md +0 -26
- package/.agent/skills/api-patterns/graphql.md +0 -41
- package/.agent/skills/api-patterns/rate-limiting.md +0 -31
- package/.agent/skills/api-patterns/response.md +0 -37
- package/.agent/skills/api-patterns/rest.md +0 -40
- package/.agent/skills/api-patterns/security-testing.md +0 -122
- package/.agent/skills/api-patterns/trpc.md +0 -41
- package/.agent/skills/api-patterns/versioning.md +0 -22
- package/.agent/skills/app-builder/agent-coordination.md +0 -71
- package/.agent/skills/app-builder/feature-building.md +0 -53
- package/.agent/skills/app-builder/project-detection.md +0 -34
- package/.agent/skills/app-builder/scaffolding.md +0 -118
- package/.agent/skills/app-builder/tech-stack.md +0 -40
- package/.agent/skills/architecture/context-discovery.md +0 -43
- package/.agent/skills/architecture/examples.md +0 -94
- package/.agent/skills/architecture/pattern-selection.md +0 -68
- package/.agent/skills/architecture/patterns-reference.md +0 -50
- package/.agent/skills/architecture/trade-off-analysis.md +0 -77
- package/.agent/skills/brainstorming/dynamic-questioning.md +0 -360
- package/.agent/skills/database-design/database-selection.md +0 -43
- package/.agent/skills/database-design/indexing.md +0 -39
- package/.agent/skills/database-design/migrations.md +0 -48
- package/.agent/skills/database-design/optimization.md +0 -36
- package/.agent/skills/database-design/orm-selection.md +0 -30
- package/.agent/skills/database-design/schema-design.md +0 -56
- package/.agent/skills/dotnet-core-expert/SKILL.md +0 -103
- package/.agent/skills/framer-motion-animations/SKILL.md +0 -74
- package/.agent/skills/frontend-design/animation-guide.md +0 -331
- package/.agent/skills/frontend-design/color-system.md +0 -329
- package/.agent/skills/frontend-design/decision-trees.md +0 -418
- package/.agent/skills/frontend-design/motion-graphics.md +0 -306
- package/.agent/skills/frontend-design/typography-system.md +0 -363
- package/.agent/skills/frontend-design/ux-psychology.md +0 -1116
- package/.agent/skills/frontend-design/visual-effects.md +0 -383
- package/.agent/skills/game-development/2d-games/SKILL.md +0 -119
- package/.agent/skills/game-development/3d-games/SKILL.md +0 -135
- package/.agent/skills/game-development/SKILL.md +0 -236
- package/.agent/skills/game-development/game-art/SKILL.md +0 -185
- package/.agent/skills/game-development/game-audio/SKILL.md +0 -190
- package/.agent/skills/game-development/game-design/SKILL.md +0 -129
- package/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
- package/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
- package/.agent/skills/game-development/pc-games/SKILL.md +0 -144
- package/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
- package/.agent/skills/game-development/web-games/SKILL.md +0 -150
- package/.agent/skills/intelligent-routing/router-manifest.md +0 -65
- package/.agent/skills/mobile-design/decision-trees.md +0 -516
- package/.agent/skills/mobile-design/mobile-backend.md +0 -491
- package/.agent/skills/mobile-design/mobile-color-system.md +0 -420
- package/.agent/skills/mobile-design/mobile-debugging.md +0 -122
- package/.agent/skills/mobile-design/mobile-design-thinking.md +0 -357
- package/.agent/skills/mobile-design/mobile-navigation.md +0 -458
- package/.agent/skills/mobile-design/mobile-performance.md +0 -767
- package/.agent/skills/mobile-design/mobile-testing.md +0 -356
- package/.agent/skills/mobile-design/mobile-typography.md +0 -433
- package/.agent/skills/mobile-design/platform-android.md +0 -666
- package/.agent/skills/mobile-design/platform-ios.md +0 -561
- package/.agent/skills/mobile-design/touch-psychology.md +0 -537
- package/.agent/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +0 -312
- package/.agent/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +0 -240
- package/.agent/skills/nextjs-react-expert/3-server-server-side-performance.md +0 -490
- package/.agent/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +0 -264
- package/.agent/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +0 -581
- package/.agent/skills/nextjs-react-expert/6-rendering-rendering-performance.md +0 -432
- package/.agent/skills/nextjs-react-expert/7-js-javascript-performance.md +0 -684
- package/.agent/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +0 -150
- package/.agent/skills/vulnerability-scanner/checklists.md +0 -121
|
@@ -1,72 +1,293 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rust-pro
|
|
3
|
-
description: Master Rust 1.75+ with modern async patterns,
|
|
3
|
+
description: Master Rust 1.75+ with modern async patterns, ownership/borrowing, lifetimes, traits, error handling with thiserror/anyhow, async Tokio runtime, axum web framework, serde serialization, and systems programming. Use when building Rust services, CLI tools, WebAssembly, or performance-critical systems.
|
|
4
4
|
allowed-tools: Read, Write, Edit, Glob, Grep
|
|
5
|
-
version:
|
|
6
|
-
last-updated: 2026-03-
|
|
5
|
+
version: 2.0.0
|
|
6
|
+
last-updated: 2026-03-30
|
|
7
7
|
applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
# Rust
|
|
11
|
-
|
|
12
|
-
> Rust's compiler is not your enemy. It is the most thorough code reviewer you will ever have.
|
|
13
|
-
> Learn to read its errors as design feedback, not roadblocks.
|
|
10
|
+
# Rust Pro — Rust 1.75+ Systems Mastery
|
|
14
11
|
|
|
15
12
|
---
|
|
16
13
|
|
|
17
|
-
## Ownership
|
|
14
|
+
## Ownership & Borrowing
|
|
18
15
|
|
|
19
|
-
The
|
|
16
|
+
### The Three Rules
|
|
20
17
|
|
|
21
18
|
```rust
|
|
22
|
-
// Rule 1: Each value has exactly
|
|
19
|
+
// Rule 1: Each value has exactly ONE owner
|
|
23
20
|
let s1 = String::from("hello");
|
|
24
|
-
let s2 = s1;
|
|
25
|
-
// println!("{}"
|
|
21
|
+
let s2 = s1; // s1 is MOVED to s2 — s1 is no longer valid
|
|
22
|
+
// println!("{s1}"); // ❌ compile error: value borrowed after move
|
|
23
|
+
|
|
24
|
+
// Rule 2: You can have EITHER one mutable reference OR any number of immutable references
|
|
25
|
+
let mut data = vec![1, 2, 3];
|
|
26
|
+
let r1 = &data; // ✅ immutable borrow
|
|
27
|
+
let r2 = &data; // ✅ second immutable borrow — fine
|
|
28
|
+
// let r3 = &mut data; // ❌ compile error: cannot borrow as mutable while immutable borrows exist
|
|
29
|
+
println!("{r1:?} {r2:?}");
|
|
30
|
+
// r1 and r2 go out of scope here (NLL — Non-Lexical Lifetimes)
|
|
31
|
+
let r3 = &mut data; // ✅ now fine — no immutable borrows active
|
|
32
|
+
r3.push(4);
|
|
33
|
+
|
|
34
|
+
// Rule 3: References must always be valid (no dangling pointers)
|
|
35
|
+
// fn dangling() -> &String { // ❌ compile error
|
|
36
|
+
// let s = String::from("hello");
|
|
37
|
+
// &s // s is dropped at end of function — reference would dangle
|
|
38
|
+
// }
|
|
39
|
+
fn not_dangling() -> String {
|
|
40
|
+
String::from("hello") // ✅ return owned value
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Common Ownership Patterns
|
|
45
|
+
|
|
46
|
+
```rust
|
|
47
|
+
// Clone when you need independent copies (has a cost — measure)
|
|
48
|
+
let original = vec![1, 2, 3];
|
|
49
|
+
let copy = original.clone(); // deep copy — both are independent
|
|
50
|
+
|
|
51
|
+
// Rc<T> — shared ownership (single-threaded)
|
|
52
|
+
use std::rc::Rc;
|
|
53
|
+
let shared = Rc::new(vec![1, 2, 3]);
|
|
54
|
+
let also_shared = Rc::clone(&shared); // cheap reference count increment
|
|
55
|
+
// Both shared and also_shared point to the same data
|
|
56
|
+
|
|
57
|
+
// Arc<T> — shared ownership (thread-safe)
|
|
58
|
+
use std::sync::Arc;
|
|
59
|
+
let thread_safe = Arc::new(vec![1, 2, 3]);
|
|
60
|
+
let for_thread = Arc::clone(&thread_safe);
|
|
61
|
+
std::thread::spawn(move || {
|
|
62
|
+
println!("{for_thread:?}");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Cow<T> — Clone on Write (zero-copy when not modified)
|
|
66
|
+
use std::borrow::Cow;
|
|
67
|
+
fn process(input: &str) -> Cow<'_, str> {
|
|
68
|
+
if input.contains("bad") {
|
|
69
|
+
Cow::Owned(input.replace("bad", "good")) // allocated only if needed
|
|
70
|
+
} else {
|
|
71
|
+
Cow::Borrowed(input) // zero-copy
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Lifetimes
|
|
79
|
+
|
|
80
|
+
```rust
|
|
81
|
+
// Lifetime annotations tell the compiler how long references are valid
|
|
82
|
+
// They DON'T change how long values live — they DESCRIBE existing relationships
|
|
83
|
+
|
|
84
|
+
// ✅ Explicit lifetime: return value lives as long as the input
|
|
85
|
+
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
|
86
|
+
if x.len() > y.len() { x } else { y }
|
|
87
|
+
}
|
|
26
88
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
89
|
+
// Struct with references (requires lifetime annotation)
|
|
90
|
+
struct Config<'a> {
|
|
91
|
+
name: &'a str,
|
|
92
|
+
version: &'a str,
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
impl<'a> Config<'a> {
|
|
96
|
+
fn display(&self) -> String {
|
|
97
|
+
format!("{} v{}", self.name, self.version)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 'static lifetime — lives for the entire program
|
|
102
|
+
let s: &'static str = "I live forever"; // string literals are 'static
|
|
103
|
+
// Owned types satisfy 'static (they own their data)
|
|
104
|
+
fn takes_static(s: String) { /* String is 'static because it owns its data */ }
|
|
105
|
+
|
|
106
|
+
// ❌ HALLUCINATION TRAP: Lifetime elision rules handle most cases
|
|
107
|
+
// Don't add lifetimes unless the compiler asks for them
|
|
108
|
+
// The compiler tells you exactly which annotations are needed
|
|
34
109
|
```
|
|
35
110
|
|
|
36
111
|
---
|
|
37
112
|
|
|
38
113
|
## Error Handling
|
|
39
114
|
|
|
40
|
-
|
|
115
|
+
### The `?` Operator & Result
|
|
41
116
|
|
|
42
117
|
```rust
|
|
118
|
+
use std::fs;
|
|
43
119
|
use std::io;
|
|
120
|
+
|
|
121
|
+
// ✅ Propagate errors with ?
|
|
122
|
+
fn read_config(path: &str) -> Result<Config, io::Error> {
|
|
123
|
+
let content = fs::read_to_string(path)?; // returns early on error
|
|
124
|
+
let config: Config = serde_json::from_str(&content)?;
|
|
125
|
+
Ok(config)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ❌ HALLUCINATION TRAP: NEVER use .unwrap() in production code
|
|
129
|
+
// .unwrap() panics on error — crashes the entire program
|
|
130
|
+
// ❌ let file = File::open("config.json").unwrap();
|
|
131
|
+
// ✅ let file = File::open("config.json")?;
|
|
132
|
+
// ✅ let file = File::open("config.json").unwrap_or_default();
|
|
133
|
+
// ✅ let file = File::open("config.json").map_err(|e| AppError::Io(e))?;
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### thiserror (Library Errors)
|
|
137
|
+
|
|
138
|
+
```rust
|
|
139
|
+
// thiserror — for library code (structured error types)
|
|
44
140
|
use thiserror::Error;
|
|
45
141
|
|
|
46
|
-
|
|
47
|
-
#[derive(Error, Debug)]
|
|
142
|
+
#[derive(Debug, Error)]
|
|
48
143
|
pub enum AppError {
|
|
49
|
-
#[error("User {0} not found")]
|
|
50
|
-
UserNotFound(String),
|
|
51
|
-
|
|
52
144
|
#[error("Database error: {0}")]
|
|
53
145
|
Database(#[from] sqlx::Error),
|
|
54
|
-
|
|
146
|
+
|
|
147
|
+
#[error("Validation error: {field} — {message}")]
|
|
148
|
+
Validation { field: String, message: String },
|
|
149
|
+
|
|
150
|
+
#[error("Not found: {0}")]
|
|
151
|
+
NotFound(String),
|
|
152
|
+
|
|
153
|
+
#[error("Unauthorized")]
|
|
154
|
+
Unauthorized,
|
|
155
|
+
|
|
55
156
|
#[error("IO error: {0}")]
|
|
56
|
-
Io(#[from] io::Error),
|
|
157
|
+
Io(#[from] std::io::Error),
|
|
158
|
+
|
|
159
|
+
#[error("JSON error: {0}")]
|
|
160
|
+
Json(#[from] serde_json::Error),
|
|
57
161
|
}
|
|
58
162
|
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
163
|
+
// #[from] auto-implements From<sqlx::Error> for AppError
|
|
164
|
+
// So sqlx errors can be propagated with ? automatically
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### anyhow (Application Errors)
|
|
168
|
+
|
|
169
|
+
```rust
|
|
170
|
+
// anyhow — for application code (quick error propagation)
|
|
171
|
+
use anyhow::{Context, Result, bail, ensure};
|
|
172
|
+
|
|
173
|
+
fn load_config(path: &str) -> Result<Config> {
|
|
174
|
+
let content = fs::read_to_string(path)
|
|
175
|
+
.context(format!("Failed to read config from {path}"))?;
|
|
176
|
+
|
|
177
|
+
let config: Config = serde_json::from_str(&content)
|
|
178
|
+
.context("Invalid JSON in config file")?;
|
|
179
|
+
|
|
180
|
+
ensure!(config.port > 0, "Port must be positive, got {}", config.port);
|
|
181
|
+
|
|
182
|
+
if config.name.is_empty() {
|
|
183
|
+
bail!("Config name cannot be empty");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
Ok(config)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Use thiserror for libraries, anyhow for applications
|
|
190
|
+
// ❌ HALLUCINATION TRAP: Don't use anyhow in library crates
|
|
191
|
+
// Libraries should expose structured error types (thiserror)
|
|
192
|
+
// anyhow erases type information — callers can't match on specific errors
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Traits
|
|
198
|
+
|
|
199
|
+
### Defining & Implementing
|
|
200
|
+
|
|
201
|
+
```rust
|
|
202
|
+
trait Summarizable {
|
|
203
|
+
fn summary(&self) -> String;
|
|
204
|
+
|
|
205
|
+
// Default implementation
|
|
206
|
+
fn preview(&self) -> String {
|
|
207
|
+
let s = self.summary();
|
|
208
|
+
if s.len() > 50 {
|
|
209
|
+
format!("{}...", &s[..50])
|
|
210
|
+
} else {
|
|
211
|
+
s
|
|
212
|
+
}
|
|
213
|
+
}
|
|
63
214
|
}
|
|
64
215
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
216
|
+
struct Article {
|
|
217
|
+
title: String,
|
|
218
|
+
body: String,
|
|
219
|
+
author: String,
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
impl Summarizable for Article {
|
|
223
|
+
fn summary(&self) -> String {
|
|
224
|
+
format!("{} by {} — {}", self.title, self.author, &self.body[..100.min(self.body.len())])
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Trait bounds
|
|
229
|
+
fn notify(item: &impl Summarizable) {
|
|
230
|
+
println!("Breaking: {}", item.summary());
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Equivalent with generics (more flexible)
|
|
234
|
+
fn notify_generic<T: Summarizable + std::fmt::Display>(item: &T) {
|
|
235
|
+
println!("Breaking: {}", item.summary());
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// where clause (cleaner for complex bounds)
|
|
239
|
+
fn process<T, U>(t: &T, u: &U) -> String
|
|
240
|
+
where
|
|
241
|
+
T: Summarizable + Clone,
|
|
242
|
+
U: std::fmt::Debug + Send,
|
|
243
|
+
{
|
|
244
|
+
format!("{} — {:?}", t.summary(), u)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Return impl Trait (hide concrete type)
|
|
248
|
+
fn make_summarizer() -> impl Summarizable {
|
|
249
|
+
Article { title: "News".into(), body: "Content".into(), author: "Author".into() }
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Common Standard Traits
|
|
254
|
+
|
|
255
|
+
```rust
|
|
256
|
+
// Derive common traits
|
|
257
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
258
|
+
struct Point {
|
|
259
|
+
x: i32,
|
|
260
|
+
y: i32,
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Display — for user-facing output
|
|
264
|
+
use std::fmt;
|
|
265
|
+
impl fmt::Display for Point {
|
|
266
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
267
|
+
write!(f, "({}, {})", self.x, self.y)
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// From/Into — type conversion
|
|
272
|
+
impl From<(i32, i32)> for Point {
|
|
273
|
+
fn from((x, y): (i32, i32)) -> Self {
|
|
274
|
+
Point { x, y }
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
let p: Point = (10, 20).into(); // uses From automatically
|
|
278
|
+
|
|
279
|
+
// Iterator
|
|
280
|
+
struct Counter { count: u32, max: u32 }
|
|
281
|
+
impl Iterator for Counter {
|
|
282
|
+
type Item = u32;
|
|
283
|
+
fn next(&mut self) -> Option<Self::Item> {
|
|
284
|
+
if self.count < self.max {
|
|
285
|
+
self.count += 1;
|
|
286
|
+
Some(self.count)
|
|
287
|
+
} else {
|
|
288
|
+
None
|
|
289
|
+
}
|
|
290
|
+
}
|
|
70
291
|
}
|
|
71
292
|
```
|
|
72
293
|
|
|
@@ -74,167 +295,329 @@ match get_user("123").await {
|
|
|
74
295
|
|
|
75
296
|
## Async with Tokio
|
|
76
297
|
|
|
298
|
+
### Runtime Setup
|
|
299
|
+
|
|
77
300
|
```rust
|
|
78
|
-
|
|
301
|
+
// Cargo.toml
|
|
302
|
+
// [dependencies]
|
|
303
|
+
// tokio = { version = "1", features = ["full"] }
|
|
79
304
|
|
|
80
305
|
#[tokio::main]
|
|
81
306
|
async fn main() {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
307
|
+
let result = fetch_data("https://api.example.com/data").await;
|
|
308
|
+
println!("{result:?}");
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// For library code — don't use #[tokio::main], let the caller choose the runtime
|
|
312
|
+
pub async fn fetch_data(url: &str) -> Result<String> {
|
|
313
|
+
let response = reqwest::get(url).await?;
|
|
314
|
+
let body = response.text().await?;
|
|
315
|
+
Ok(body)
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Concurrent Tasks
|
|
320
|
+
|
|
321
|
+
```rust
|
|
322
|
+
use tokio::task;
|
|
323
|
+
|
|
324
|
+
// Spawn concurrent tasks
|
|
325
|
+
async fn parallel_fetch() -> Result<(Users, Posts)> {
|
|
326
|
+
let users_handle = task::spawn(async { fetch_users().await });
|
|
327
|
+
let posts_handle = task::spawn(async { fetch_posts().await });
|
|
328
|
+
|
|
329
|
+
let users = users_handle.await??; // first ? for JoinError, second for app error
|
|
330
|
+
let posts = posts_handle.await??;
|
|
331
|
+
|
|
332
|
+
Ok((users, posts))
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// tokio::join! — run concurrently, wait for all
|
|
336
|
+
async fn fetch_all() -> Result<(Users, Posts, Analytics)> {
|
|
337
|
+
let (users, posts, analytics) = tokio::join!(
|
|
338
|
+
fetch_users(),
|
|
339
|
+
fetch_posts(),
|
|
340
|
+
fetch_analytics(),
|
|
86
341
|
);
|
|
342
|
+
Ok((users?, posts?, analytics?))
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// tokio::select! — race multiple futures, take first to complete
|
|
346
|
+
async fn fetch_with_timeout(url: &str) -> Result<String> {
|
|
347
|
+
tokio::select! {
|
|
348
|
+
result = fetch_data(url) => result,
|
|
349
|
+
_ = tokio::time::sleep(Duration::from_secs(5)) => {
|
|
350
|
+
Err(anyhow!("Request timed out after 5s"))
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ❌ HALLUCINATION TRAP: tokio::spawn requires 'static + Send
|
|
356
|
+
// You cannot spawn a task referencing local variables without Arc/clone
|
|
357
|
+
// ❌ let data = &local_data;
|
|
358
|
+
// tokio::spawn(async { process(data) }); // ❌ data doesn't live long enough
|
|
359
|
+
// ✅ let data = Arc::new(local_data);
|
|
360
|
+
// let data_clone = Arc::clone(&data);
|
|
361
|
+
// tokio::spawn(async move { process(&data_clone) });
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Channels
|
|
365
|
+
|
|
366
|
+
```rust
|
|
367
|
+
use tokio::sync::{mpsc, oneshot, broadcast};
|
|
87
368
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
369
|
+
// mpsc — Multiple Producer, Single Consumer
|
|
370
|
+
async fn worker_pattern() {
|
|
371
|
+
let (tx, mut rx) = mpsc::channel::<String>(32); // buffer size
|
|
372
|
+
|
|
373
|
+
tokio::spawn(async move {
|
|
374
|
+
tx.send("hello".to_string()).await.unwrap();
|
|
375
|
+
tx.send("world".to_string()).await.unwrap();
|
|
91
376
|
});
|
|
92
377
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
let result = timeout(Duration::from_secs(5), slow_operation()).await;
|
|
96
|
-
match result {
|
|
97
|
-
Ok(Ok(val)) => println!("Got: {}", val),
|
|
98
|
-
Ok(Err(e)) => eprintln!("Operation failed: {}", e),
|
|
99
|
-
Err(_) => eprintln!("Timed out"),
|
|
378
|
+
while let Some(msg) = rx.recv().await {
|
|
379
|
+
println!("Got: {msg}");
|
|
100
380
|
}
|
|
101
381
|
}
|
|
382
|
+
|
|
383
|
+
// oneshot — single response (request/response pattern)
|
|
384
|
+
async fn request_response() {
|
|
385
|
+
let (tx, rx) = oneshot::channel::<String>();
|
|
386
|
+
|
|
387
|
+
tokio::spawn(async move {
|
|
388
|
+
let result = expensive_computation().await;
|
|
389
|
+
tx.send(result).unwrap();
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
let response = rx.await.unwrap();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Mutex (async-safe)
|
|
396
|
+
use tokio::sync::Mutex;
|
|
397
|
+
let shared_state = Arc::new(Mutex::new(Vec::new()));
|
|
398
|
+
|
|
399
|
+
let state = Arc::clone(&shared_state);
|
|
400
|
+
tokio::spawn(async move {
|
|
401
|
+
let mut guard = state.lock().await;
|
|
402
|
+
guard.push("item");
|
|
403
|
+
}); // lock released when guard is dropped
|
|
102
404
|
```
|
|
103
405
|
|
|
104
406
|
---
|
|
105
407
|
|
|
106
|
-
##
|
|
408
|
+
## Axum Web Framework
|
|
409
|
+
|
|
410
|
+
### Basic Server
|
|
107
411
|
|
|
108
412
|
```rust
|
|
109
|
-
use axum::{
|
|
110
|
-
|
|
413
|
+
use axum::{
|
|
414
|
+
extract::{Path, Query, State, Json},
|
|
415
|
+
http::StatusCode,
|
|
416
|
+
response::IntoResponse,
|
|
417
|
+
routing::{get, post, delete},
|
|
418
|
+
Router,
|
|
419
|
+
};
|
|
420
|
+
use serde::{Deserialize, Serialize};
|
|
111
421
|
|
|
112
422
|
#[derive(Clone)]
|
|
113
423
|
struct AppState {
|
|
114
|
-
db:
|
|
424
|
+
db: sqlx::PgPool,
|
|
115
425
|
}
|
|
116
426
|
|
|
117
427
|
#[tokio::main]
|
|
118
428
|
async fn main() {
|
|
119
|
-
let
|
|
429
|
+
let pool = sqlx::PgPool::connect("postgres://localhost/mydb").await.unwrap();
|
|
430
|
+
let state = AppState { db: pool };
|
|
120
431
|
|
|
121
432
|
let app = Router::new()
|
|
122
433
|
.route("/users", get(list_users).post(create_user))
|
|
123
|
-
.route("/users
|
|
434
|
+
.route("/users/{id}", get(get_user).delete(delete_user))
|
|
124
435
|
.with_state(state);
|
|
125
436
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
app,
|
|
129
|
-
).await.unwrap();
|
|
437
|
+
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
|
438
|
+
axum::serve(listener, app).await.unwrap();
|
|
130
439
|
}
|
|
131
440
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
) -> Result<Json<User>, StatusCode> {
|
|
136
|
-
state.db.find_user(&id).await
|
|
137
|
-
.map(Json)
|
|
138
|
-
.map_err(|_| StatusCode::NOT_FOUND)
|
|
139
|
-
}
|
|
441
|
+
// ❌ HALLUCINATION TRAP: axum 0.7+ uses {id} not :id for path params
|
|
442
|
+
// ❌ .route("/users/:id", ...) ← old syntax
|
|
443
|
+
// ✅ .route("/users/{id}", ...) ← axum 0.7+
|
|
140
444
|
```
|
|
141
445
|
|
|
142
|
-
|
|
446
|
+
### Handlers
|
|
143
447
|
|
|
144
|
-
|
|
448
|
+
```rust
|
|
449
|
+
#[derive(Deserialize)]
|
|
450
|
+
struct ListParams {
|
|
451
|
+
page: Option<u32>,
|
|
452
|
+
limit: Option<u32>,
|
|
453
|
+
}
|
|
145
454
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
455
|
+
async fn list_users(
|
|
456
|
+
State(state): State<AppState>,
|
|
457
|
+
Query(params): Query<ListParams>,
|
|
458
|
+
) -> Result<Json<Vec<User>>, AppError> {
|
|
459
|
+
let page = params.page.unwrap_or(1);
|
|
460
|
+
let limit = params.limit.unwrap_or(20).min(100);
|
|
461
|
+
let offset = (page - 1) * limit;
|
|
462
|
+
|
|
463
|
+
let users = sqlx::query_as!(
|
|
464
|
+
User,
|
|
465
|
+
"SELECT id, name, email FROM users ORDER BY id LIMIT $1 OFFSET $2",
|
|
466
|
+
limit as i64,
|
|
467
|
+
offset as i64,
|
|
468
|
+
)
|
|
469
|
+
.fetch_all(&state.db)
|
|
470
|
+
.await?;
|
|
471
|
+
|
|
472
|
+
Ok(Json(users))
|
|
473
|
+
}
|
|
152
474
|
|
|
153
|
-
|
|
475
|
+
#[derive(Deserialize)]
|
|
476
|
+
struct CreateUserPayload {
|
|
477
|
+
name: String,
|
|
478
|
+
email: String,
|
|
479
|
+
}
|
|
154
480
|
|
|
155
|
-
|
|
481
|
+
async fn create_user(
|
|
482
|
+
State(state): State<AppState>,
|
|
483
|
+
Json(payload): Json<CreateUserPayload>,
|
|
484
|
+
) -> Result<(StatusCode, Json<User>), AppError> {
|
|
485
|
+
let user = sqlx::query_as!(
|
|
486
|
+
User,
|
|
487
|
+
"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email",
|
|
488
|
+
payload.name,
|
|
489
|
+
payload.email,
|
|
490
|
+
)
|
|
491
|
+
.fetch_one(&state.db)
|
|
492
|
+
.await?;
|
|
493
|
+
|
|
494
|
+
Ok((StatusCode::CREATED, Json(user)))
|
|
495
|
+
}
|
|
156
496
|
|
|
497
|
+
async fn get_user(
|
|
498
|
+
State(state): State<AppState>,
|
|
499
|
+
Path(id): Path<i32>,
|
|
500
|
+
) -> Result<Json<User>, AppError> {
|
|
501
|
+
let user = sqlx::query_as!(User, "SELECT id, name, email FROM users WHERE id = $1", id)
|
|
502
|
+
.fetch_optional(&state.db)
|
|
503
|
+
.await?
|
|
504
|
+
.ok_or(AppError::NotFound(format!("User {id}")))?;
|
|
505
|
+
|
|
506
|
+
Ok(Json(user))
|
|
507
|
+
}
|
|
157
508
|
```
|
|
158
|
-
src/
|
|
159
|
-
main.rs Entry point — thin, just wires up the server
|
|
160
|
-
lib.rs Re-exports for library usage
|
|
161
|
-
handlers/ HTTP request handlers (thin — parse, call service, return response)
|
|
162
|
-
services/ Business logic (no HTTP awareness)
|
|
163
|
-
repositories/ Database queries
|
|
164
|
-
models/ Data structures and validation
|
|
165
|
-
errors.rs Unified error type
|
|
166
|
-
config.rs Settings loaded from environment
|
|
167
509
|
|
|
168
|
-
|
|
169
|
-
```
|
|
510
|
+
### Error Handling in Axum
|
|
170
511
|
|
|
171
|
-
|
|
512
|
+
```rust
|
|
513
|
+
use axum::response::{IntoResponse, Response};
|
|
172
514
|
|
|
173
|
-
|
|
515
|
+
#[derive(Debug, thiserror::Error)]
|
|
516
|
+
pub enum AppError {
|
|
517
|
+
#[error("Not found: {0}")]
|
|
518
|
+
NotFound(String),
|
|
519
|
+
#[error("Validation: {0}")]
|
|
520
|
+
Validation(String),
|
|
521
|
+
#[error("Database: {0}")]
|
|
522
|
+
Database(#[from] sqlx::Error),
|
|
523
|
+
#[error("Internal: {0}")]
|
|
524
|
+
Internal(#[from] anyhow::Error),
|
|
525
|
+
}
|
|
174
526
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
527
|
+
impl IntoResponse for AppError {
|
|
528
|
+
fn into_response(self) -> Response {
|
|
529
|
+
let (status, message) = match &self {
|
|
530
|
+
AppError::NotFound(msg) => (StatusCode::NOT_FOUND, msg.clone()),
|
|
531
|
+
AppError::Validation(msg) => (StatusCode::BAD_REQUEST, msg.clone()),
|
|
532
|
+
AppError::Database(e) => {
|
|
533
|
+
tracing::error!("DB error: {e}"); // log internal details
|
|
534
|
+
(StatusCode::INTERNAL_SERVER_ERROR, "Database error".to_string())
|
|
535
|
+
}
|
|
536
|
+
AppError::Internal(e) => {
|
|
537
|
+
tracing::error!("Internal error: {e}");
|
|
538
|
+
(StatusCode::INTERNAL_SERVER_ERROR, "Internal error".to_string())
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
(status, Json(serde_json::json!({ "error": message }))).into_response()
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
```
|
|
185
546
|
|
|
186
547
|
---
|
|
187
548
|
|
|
188
|
-
##
|
|
189
|
-
|
|
190
|
-
When this skill produces or reviews code, structure your output as follows:
|
|
549
|
+
## Serde (Serialization)
|
|
191
550
|
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
Skill: Rust Pro
|
|
195
|
-
Language: [detected language / framework]
|
|
196
|
-
Scope: [N files · N functions]
|
|
197
|
-
─────────────────────────────────────────────────
|
|
198
|
-
✅ Passed: [checks that passed, or "All clean"]
|
|
199
|
-
⚠️ Warnings: [non-blocking issues, or "None"]
|
|
200
|
-
❌ Blocked: [blocking issues requiring fix, or "None"]
|
|
201
|
-
─────────────────────────────────────────────────
|
|
202
|
-
VBC status: PENDING → VERIFIED
|
|
203
|
-
Evidence: [test output / lint pass / compile success]
|
|
204
|
-
```
|
|
551
|
+
```rust
|
|
552
|
+
use serde::{Deserialize, Serialize};
|
|
205
553
|
|
|
206
|
-
|
|
207
|
-
|
|
554
|
+
#[derive(Debug, Serialize, Deserialize)]
|
|
555
|
+
#[serde(rename_all = "camelCase")] // JSON uses camelCase
|
|
556
|
+
struct UserResponse {
|
|
557
|
+
user_id: i32, // serialized as "userId"
|
|
558
|
+
full_name: String, // serialized as "fullName"
|
|
559
|
+
email: String,
|
|
208
560
|
|
|
561
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
562
|
+
phone: Option<String>, // omitted from JSON if None
|
|
209
563
|
|
|
210
|
-
|
|
564
|
+
#[serde(default)] // defaults to 0 if missing in input
|
|
565
|
+
login_count: u32,
|
|
211
566
|
|
|
212
|
-
|
|
567
|
+
#[serde(rename = "type")] // rename for reserved keywords
|
|
568
|
+
user_type: String,
|
|
213
569
|
|
|
214
|
-
|
|
215
|
-
|
|
570
|
+
#[serde(skip)] // never serialized/deserialized
|
|
571
|
+
internal_token: String,
|
|
572
|
+
}
|
|
216
573
|
|
|
217
|
-
|
|
574
|
+
// Enum serialization
|
|
575
|
+
#[derive(Serialize, Deserialize)]
|
|
576
|
+
#[serde(tag = "type", content = "data")] // adjacently tagged
|
|
577
|
+
enum Event {
|
|
578
|
+
#[serde(rename = "user_created")]
|
|
579
|
+
UserCreated { id: i32, name: String },
|
|
580
|
+
#[serde(rename = "user_deleted")]
|
|
581
|
+
UserDeleted { id: i32 },
|
|
582
|
+
}
|
|
583
|
+
// Serializes as: {"type": "user_created", "data": {"id": 1, "name": "Alice"}}
|
|
584
|
+
```
|
|
218
585
|
|
|
219
|
-
|
|
220
|
-
2. **`.clone()` everywhere** — blindly copying data instead of reasoning about lifetimes and borrows.
|
|
221
|
-
3. **`Arc<Mutex<T>>` overuse** — wrapping everything in locks instead of passing ownership or using channels.
|
|
222
|
-
4. **Blocking the Tokio runtime** — using `std::thread::sleep` or sync I/O inside an `async` function.
|
|
223
|
-
5. **Over-complex trait bounds** — creating massive generic trait puzzles instead of concrete types when generics aren't needed.
|
|
586
|
+
---
|
|
224
587
|
|
|
225
|
-
|
|
588
|
+
## Iterator Patterns
|
|
226
589
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
590
|
+
```rust
|
|
591
|
+
let numbers = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
|
592
|
+
|
|
593
|
+
// Chain operations (lazy — no allocation until collect)
|
|
594
|
+
let result: Vec<i32> = numbers.iter()
|
|
595
|
+
.filter(|&&n| n % 2 == 0) // keep even
|
|
596
|
+
.map(|&n| n * n) // square
|
|
597
|
+
.take(3) // first 3
|
|
598
|
+
.collect(); // [4, 16, 36]
|
|
599
|
+
|
|
600
|
+
// fold (reduce)
|
|
601
|
+
let sum: i32 = numbers.iter().fold(0, |acc, &n| acc + n);
|
|
602
|
+
|
|
603
|
+
// find / position
|
|
604
|
+
let first_even = numbers.iter().find(|&&n| n % 2 == 0); // Some(&2)
|
|
605
|
+
let pos = numbers.iter().position(|&n| n > 5); // Some(5)
|
|
606
|
+
|
|
607
|
+
// chunk / window
|
|
608
|
+
let chunks: Vec<&[i32]> = numbers.chunks(3).collect();
|
|
609
|
+
// [[1,2,3], [4,5,6], [7,8,9], [10]]
|
|
610
|
+
|
|
611
|
+
let windows: Vec<&[i32]> = numbers.windows(3).collect();
|
|
612
|
+
// [[1,2,3], [2,3,4], [3,4,5], ...]
|
|
613
|
+
|
|
614
|
+
// Collecting into HashMap
|
|
615
|
+
use std::collections::HashMap;
|
|
616
|
+
let word_counts: HashMap<&str, usize> = words.iter()
|
|
617
|
+
.fold(HashMap::new(), |mut map, word| {
|
|
618
|
+
*map.entry(word.as_str()).or_insert(0) += 1;
|
|
619
|
+
map
|
|
620
|
+
});
|
|
234
621
|
```
|
|
235
622
|
|
|
236
|
-
### 🛑 Verification-Before-Completion (VBC) Protocol
|
|
237
623
|
|
|
238
|
-
**CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
|
|
239
|
-
- ❌ **Forbidden:** Concluding your work because the logic seems sound or assuming `cargo check` is enough for complex runtime logic.
|
|
240
|
-
- ✅ **Required:** You are explicitly forbidden from completing your Rust task without providing **concrete terminal evidence** that `cargo test` passes or the binary runs successfully (`cargo run`) without panics. The Borrow Checker passing is step one; runtime verification is mandatory before completion.
|