@booklib/core 2.0.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/.cursor/rules/booklib-standards.mdc +40 -0
- package/.gemini/context.md +372 -0
- package/AGENTS.md +166 -0
- package/CHANGELOG.md +226 -0
- package/CLAUDE.md +81 -0
- package/CODE_OF_CONDUCT.md +31 -0
- package/CONTRIBUTING.md +304 -0
- package/LICENSE +21 -0
- package/PLAN.md +28 -0
- package/README.ja.md +198 -0
- package/README.ko.md +198 -0
- package/README.md +503 -0
- package/README.pt-BR.md +198 -0
- package/README.uk.md +241 -0
- package/README.zh-CN.md +198 -0
- package/SECURITY.md +9 -0
- package/agents/architecture-reviewer.md +136 -0
- package/agents/booklib-reviewer.md +90 -0
- package/agents/data-reviewer.md +107 -0
- package/agents/jvm-reviewer.md +146 -0
- package/agents/python-reviewer.md +128 -0
- package/agents/rust-reviewer.md +115 -0
- package/agents/ts-reviewer.md +110 -0
- package/agents/ui-reviewer.md +117 -0
- package/assets/logo.svg +36 -0
- package/bin/booklib-mcp.js +304 -0
- package/bin/booklib.js +1705 -0
- package/bin/skills.cjs +1292 -0
- package/booklib-router.mdc +36 -0
- package/booklib.config.json +19 -0
- package/commands/animation-at-work.md +10 -0
- package/commands/clean-code-reviewer.md +10 -0
- package/commands/data-intensive-patterns.md +10 -0
- package/commands/data-pipelines.md +10 -0
- package/commands/design-patterns.md +10 -0
- package/commands/domain-driven-design.md +10 -0
- package/commands/effective-java.md +10 -0
- package/commands/effective-kotlin.md +10 -0
- package/commands/effective-python.md +10 -0
- package/commands/effective-typescript.md +10 -0
- package/commands/kotlin-in-action.md +10 -0
- package/commands/lean-startup.md +10 -0
- package/commands/microservices-patterns.md +10 -0
- package/commands/programming-with-rust.md +10 -0
- package/commands/refactoring-ui.md +10 -0
- package/commands/rust-in-action.md +10 -0
- package/commands/skill-router.md +10 -0
- package/commands/spring-boot-in-action.md +10 -0
- package/commands/storytelling-with-data.md +10 -0
- package/commands/system-design-interview.md +10 -0
- package/commands/using-asyncio-python.md +10 -0
- package/commands/web-scraping-python.md +10 -0
- package/community/registry.json +1616 -0
- package/hooks/hooks.json +23 -0
- package/hooks/posttooluse-capture.mjs +67 -0
- package/hooks/suggest.js +153 -0
- package/lib/agent-behaviors.js +40 -0
- package/lib/agent-detector.js +96 -0
- package/lib/config-loader.js +39 -0
- package/lib/conflict-resolver.js +148 -0
- package/lib/context-builder.js +574 -0
- package/lib/discovery-engine.js +298 -0
- package/lib/doctor/hook-installer.js +83 -0
- package/lib/doctor/usage-tracker.js +87 -0
- package/lib/engine/ai-features.js +253 -0
- package/lib/engine/auditor.js +103 -0
- package/lib/engine/bm25-index.js +178 -0
- package/lib/engine/capture.js +120 -0
- package/lib/engine/corrections.js +198 -0
- package/lib/engine/doctor.js +195 -0
- package/lib/engine/graph-injector.js +137 -0
- package/lib/engine/graph.js +161 -0
- package/lib/engine/handoff.js +405 -0
- package/lib/engine/indexer.js +242 -0
- package/lib/engine/parser.js +53 -0
- package/lib/engine/query-expander.js +42 -0
- package/lib/engine/reranker.js +40 -0
- package/lib/engine/rrf.js +59 -0
- package/lib/engine/scanner.js +151 -0
- package/lib/engine/searcher.js +139 -0
- package/lib/engine/session-coordinator.js +306 -0
- package/lib/engine/session-manager.js +429 -0
- package/lib/engine/synthesizer.js +70 -0
- package/lib/installer.js +70 -0
- package/lib/instinct-block.js +33 -0
- package/lib/mcp-config-writer.js +88 -0
- package/lib/paths.js +57 -0
- package/lib/profiles/design.md +19 -0
- package/lib/profiles/general.md +16 -0
- package/lib/profiles/research-analysis.md +22 -0
- package/lib/profiles/software-development.md +23 -0
- package/lib/profiles/writing-content.md +19 -0
- package/lib/project-initializer.js +916 -0
- package/lib/registry/skills.js +102 -0
- package/lib/registry-searcher.js +99 -0
- package/lib/rules/rules-manager.js +169 -0
- package/lib/skill-fetcher.js +333 -0
- package/lib/well-known-builder.js +70 -0
- package/lib/wizard/index.js +404 -0
- package/lib/wizard/integration-detector.js +41 -0
- package/lib/wizard/project-detector.js +100 -0
- package/lib/wizard/prompt.js +156 -0
- package/lib/wizard/registry-embeddings.js +107 -0
- package/lib/wizard/skill-recommender.js +69 -0
- package/llms-full.txt +254 -0
- package/llms.txt +70 -0
- package/package.json +45 -0
- package/research-reports/2026-04-01-current-architecture.md +160 -0
- package/research-reports/IDEAS.md +93 -0
- package/rules/common/clean-code.md +42 -0
- package/rules/java/effective-java.md +42 -0
- package/rules/kotlin/effective-kotlin.md +37 -0
- package/rules/python/effective-python.md +38 -0
- package/rules/rust/rust.md +37 -0
- package/rules/typescript/effective-typescript.md +42 -0
- package/scripts/gen-llms-full.mjs +36 -0
- package/scripts/gen-og.mjs +142 -0
- package/scripts/validate-frontmatter.js +25 -0
- package/skills/animation-at-work/SKILL.md +270 -0
- package/skills/animation-at-work/assets/example_asset.txt +1 -0
- package/skills/animation-at-work/evals/evals.json +44 -0
- package/skills/animation-at-work/evals/results.json +13 -0
- package/skills/animation-at-work/examples/after.md +64 -0
- package/skills/animation-at-work/examples/before.md +35 -0
- package/skills/animation-at-work/references/api_reference.md +369 -0
- package/skills/animation-at-work/references/review-checklist.md +79 -0
- package/skills/animation-at-work/scripts/audit_animations.py +295 -0
- package/skills/animation-at-work/scripts/example.py +1 -0
- package/skills/clean-code-reviewer/SKILL.md +444 -0
- package/skills/clean-code-reviewer/audit.json +35 -0
- package/skills/clean-code-reviewer/evals/evals.json +185 -0
- package/skills/clean-code-reviewer/evals/results.json +13 -0
- package/skills/clean-code-reviewer/examples/after.md +48 -0
- package/skills/clean-code-reviewer/examples/before.md +33 -0
- package/skills/clean-code-reviewer/references/api_reference.md +158 -0
- package/skills/clean-code-reviewer/references/practices-catalog.md +282 -0
- package/skills/clean-code-reviewer/references/review-checklist.md +254 -0
- package/skills/clean-code-reviewer/scripts/pre-review.py +206 -0
- package/skills/data-intensive-patterns/SKILL.md +267 -0
- package/skills/data-intensive-patterns/assets/example_asset.txt +1 -0
- package/skills/data-intensive-patterns/evals/evals.json +54 -0
- package/skills/data-intensive-patterns/evals/results.json +13 -0
- package/skills/data-intensive-patterns/examples/after.md +61 -0
- package/skills/data-intensive-patterns/examples/before.md +38 -0
- package/skills/data-intensive-patterns/references/api_reference.md +34 -0
- package/skills/data-intensive-patterns/references/patterns-catalog.md +551 -0
- package/skills/data-intensive-patterns/references/review-checklist.md +193 -0
- package/skills/data-intensive-patterns/scripts/adr.py +213 -0
- package/skills/data-intensive-patterns/scripts/example.py +1 -0
- package/skills/data-pipelines/SKILL.md +259 -0
- package/skills/data-pipelines/assets/example_asset.txt +1 -0
- package/skills/data-pipelines/evals/evals.json +45 -0
- package/skills/data-pipelines/evals/results.json +13 -0
- package/skills/data-pipelines/examples/after.md +97 -0
- package/skills/data-pipelines/examples/before.md +37 -0
- package/skills/data-pipelines/references/api_reference.md +301 -0
- package/skills/data-pipelines/references/review-checklist.md +181 -0
- package/skills/data-pipelines/scripts/example.py +1 -0
- package/skills/data-pipelines/scripts/new_pipeline.py +444 -0
- package/skills/design-patterns/SKILL.md +271 -0
- package/skills/design-patterns/assets/example_asset.txt +1 -0
- package/skills/design-patterns/evals/evals.json +46 -0
- package/skills/design-patterns/evals/results.json +13 -0
- package/skills/design-patterns/examples/after.md +52 -0
- package/skills/design-patterns/examples/before.md +29 -0
- package/skills/design-patterns/references/api_reference.md +1 -0
- package/skills/design-patterns/references/patterns-catalog.md +726 -0
- package/skills/design-patterns/references/review-checklist.md +173 -0
- package/skills/design-patterns/scripts/example.py +1 -0
- package/skills/design-patterns/scripts/scaffold.py +807 -0
- package/skills/domain-driven-design/SKILL.md +142 -0
- package/skills/domain-driven-design/assets/example_asset.txt +1 -0
- package/skills/domain-driven-design/evals/evals.json +48 -0
- package/skills/domain-driven-design/evals/results.json +13 -0
- package/skills/domain-driven-design/examples/after.md +80 -0
- package/skills/domain-driven-design/examples/before.md +43 -0
- package/skills/domain-driven-design/references/api_reference.md +1 -0
- package/skills/domain-driven-design/references/patterns-catalog.md +545 -0
- package/skills/domain-driven-design/references/review-checklist.md +158 -0
- package/skills/domain-driven-design/scripts/example.py +1 -0
- package/skills/domain-driven-design/scripts/scaffold.py +421 -0
- package/skills/effective-java/SKILL.md +227 -0
- package/skills/effective-java/assets/example_asset.txt +1 -0
- package/skills/effective-java/evals/evals.json +46 -0
- package/skills/effective-java/evals/results.json +13 -0
- package/skills/effective-java/examples/after.md +83 -0
- package/skills/effective-java/examples/before.md +37 -0
- package/skills/effective-java/references/api_reference.md +1 -0
- package/skills/effective-java/references/items-catalog.md +955 -0
- package/skills/effective-java/references/review-checklist.md +216 -0
- package/skills/effective-java/scripts/checkstyle_setup.py +211 -0
- package/skills/effective-java/scripts/example.py +1 -0
- package/skills/effective-kotlin/SKILL.md +271 -0
- package/skills/effective-kotlin/assets/example_asset.txt +1 -0
- package/skills/effective-kotlin/audit.json +29 -0
- package/skills/effective-kotlin/evals/evals.json +45 -0
- package/skills/effective-kotlin/evals/results.json +13 -0
- package/skills/effective-kotlin/examples/after.md +36 -0
- package/skills/effective-kotlin/examples/before.md +38 -0
- package/skills/effective-kotlin/references/api_reference.md +1 -0
- package/skills/effective-kotlin/references/practices-catalog.md +1228 -0
- package/skills/effective-kotlin/references/review-checklist.md +126 -0
- package/skills/effective-kotlin/scripts/example.py +1 -0
- package/skills/effective-python/SKILL.md +441 -0
- package/skills/effective-python/evals/evals.json +44 -0
- package/skills/effective-python/evals/results.json +13 -0
- package/skills/effective-python/examples/after.md +56 -0
- package/skills/effective-python/examples/before.md +40 -0
- package/skills/effective-python/ref-01-pythonic-thinking.md +202 -0
- package/skills/effective-python/ref-02-lists-and-dicts.md +146 -0
- package/skills/effective-python/ref-03-functions.md +186 -0
- package/skills/effective-python/ref-04-comprehensions-generators.md +211 -0
- package/skills/effective-python/ref-05-classes-interfaces.md +188 -0
- package/skills/effective-python/ref-06-metaclasses-attributes.md +209 -0
- package/skills/effective-python/ref-07-concurrency.md +213 -0
- package/skills/effective-python/ref-08-robustness-performance.md +248 -0
- package/skills/effective-python/ref-09-testing-debugging.md +253 -0
- package/skills/effective-python/ref-10-collaboration.md +175 -0
- package/skills/effective-python/references/api_reference.md +218 -0
- package/skills/effective-python/references/practices-catalog.md +483 -0
- package/skills/effective-python/references/review-checklist.md +190 -0
- package/skills/effective-python/scripts/lint.py +173 -0
- package/skills/effective-typescript/SKILL.md +262 -0
- package/skills/effective-typescript/audit.json +29 -0
- package/skills/effective-typescript/evals/evals.json +37 -0
- package/skills/effective-typescript/evals/results.json +13 -0
- package/skills/effective-typescript/examples/after.md +70 -0
- package/skills/effective-typescript/examples/before.md +47 -0
- package/skills/effective-typescript/references/api_reference.md +118 -0
- package/skills/effective-typescript/references/practices-catalog.md +371 -0
- package/skills/effective-typescript/scripts/review.py +169 -0
- package/skills/kotlin-in-action/SKILL.md +261 -0
- package/skills/kotlin-in-action/assets/example_asset.txt +1 -0
- package/skills/kotlin-in-action/evals/evals.json +43 -0
- package/skills/kotlin-in-action/evals/results.json +13 -0
- package/skills/kotlin-in-action/examples/after.md +53 -0
- package/skills/kotlin-in-action/examples/before.md +39 -0
- package/skills/kotlin-in-action/references/api_reference.md +1 -0
- package/skills/kotlin-in-action/references/practices-catalog.md +436 -0
- package/skills/kotlin-in-action/references/review-checklist.md +204 -0
- package/skills/kotlin-in-action/scripts/example.py +1 -0
- package/skills/kotlin-in-action/scripts/setup_detekt.py +224 -0
- package/skills/lean-startup/SKILL.md +160 -0
- package/skills/lean-startup/assets/example_asset.txt +1 -0
- package/skills/lean-startup/evals/evals.json +43 -0
- package/skills/lean-startup/evals/results.json +13 -0
- package/skills/lean-startup/examples/after.md +80 -0
- package/skills/lean-startup/examples/before.md +34 -0
- package/skills/lean-startup/references/api_reference.md +319 -0
- package/skills/lean-startup/references/review-checklist.md +137 -0
- package/skills/lean-startup/scripts/example.py +1 -0
- package/skills/lean-startup/scripts/new_experiment.py +286 -0
- package/skills/microservices-patterns/SKILL.md +384 -0
- package/skills/microservices-patterns/evals/evals.json +45 -0
- package/skills/microservices-patterns/evals/results.json +13 -0
- package/skills/microservices-patterns/examples/after.md +69 -0
- package/skills/microservices-patterns/examples/before.md +40 -0
- package/skills/microservices-patterns/references/patterns-catalog.md +391 -0
- package/skills/microservices-patterns/references/review-checklist.md +169 -0
- package/skills/microservices-patterns/scripts/new_service.py +583 -0
- package/skills/programming-with-rust/SKILL.md +209 -0
- package/skills/programming-with-rust/evals/evals.json +37 -0
- package/skills/programming-with-rust/evals/results.json +13 -0
- package/skills/programming-with-rust/examples/after.md +107 -0
- package/skills/programming-with-rust/examples/before.md +59 -0
- package/skills/programming-with-rust/references/api_reference.md +152 -0
- package/skills/programming-with-rust/references/practices-catalog.md +335 -0
- package/skills/programming-with-rust/scripts/review.py +142 -0
- package/skills/refactoring-ui/SKILL.md +362 -0
- package/skills/refactoring-ui/assets/example_asset.txt +1 -0
- package/skills/refactoring-ui/evals/evals.json +45 -0
- package/skills/refactoring-ui/evals/results.json +13 -0
- package/skills/refactoring-ui/examples/after.md +85 -0
- package/skills/refactoring-ui/examples/before.md +58 -0
- package/skills/refactoring-ui/references/api_reference.md +355 -0
- package/skills/refactoring-ui/references/review-checklist.md +114 -0
- package/skills/refactoring-ui/scripts/audit_css.py +250 -0
- package/skills/refactoring-ui/scripts/example.py +1 -0
- package/skills/rust-in-action/SKILL.md +350 -0
- package/skills/rust-in-action/evals/evals.json +38 -0
- package/skills/rust-in-action/evals/results.json +13 -0
- package/skills/rust-in-action/examples/after.md +156 -0
- package/skills/rust-in-action/examples/before.md +56 -0
- package/skills/rust-in-action/references/practices-catalog.md +346 -0
- package/skills/rust-in-action/scripts/review.py +147 -0
- package/skills/skill-router/SKILL.md +186 -0
- package/skills/skill-router/evals/evals.json +38 -0
- package/skills/skill-router/evals/results.json +13 -0
- package/skills/skill-router/examples/after.md +63 -0
- package/skills/skill-router/examples/before.md +39 -0
- package/skills/skill-router/references/api_reference.md +24 -0
- package/skills/skill-router/references/routing-heuristics.md +89 -0
- package/skills/skill-router/references/skill-catalog.md +174 -0
- package/skills/skill-router/scripts/route.py +266 -0
- package/skills/spring-boot-in-action/SKILL.md +340 -0
- package/skills/spring-boot-in-action/evals/evals.json +39 -0
- package/skills/spring-boot-in-action/evals/results.json +13 -0
- package/skills/spring-boot-in-action/examples/after.md +185 -0
- package/skills/spring-boot-in-action/examples/before.md +84 -0
- package/skills/spring-boot-in-action/references/practices-catalog.md +403 -0
- package/skills/spring-boot-in-action/scripts/review.py +184 -0
- package/skills/storytelling-with-data/SKILL.md +241 -0
- package/skills/storytelling-with-data/assets/example_asset.txt +1 -0
- package/skills/storytelling-with-data/evals/evals.json +47 -0
- package/skills/storytelling-with-data/evals/results.json +13 -0
- package/skills/storytelling-with-data/examples/after.md +50 -0
- package/skills/storytelling-with-data/examples/before.md +33 -0
- package/skills/storytelling-with-data/references/api_reference.md +379 -0
- package/skills/storytelling-with-data/references/review-checklist.md +111 -0
- package/skills/storytelling-with-data/scripts/chart_review.py +301 -0
- package/skills/storytelling-with-data/scripts/example.py +1 -0
- package/skills/system-design-interview/SKILL.md +233 -0
- package/skills/system-design-interview/assets/example_asset.txt +1 -0
- package/skills/system-design-interview/evals/evals.json +46 -0
- package/skills/system-design-interview/evals/results.json +13 -0
- package/skills/system-design-interview/examples/after.md +94 -0
- package/skills/system-design-interview/examples/before.md +27 -0
- package/skills/system-design-interview/references/api_reference.md +582 -0
- package/skills/system-design-interview/references/review-checklist.md +201 -0
- package/skills/system-design-interview/scripts/example.py +1 -0
- package/skills/system-design-interview/scripts/new_design.py +421 -0
- package/skills/using-asyncio-python/SKILL.md +290 -0
- package/skills/using-asyncio-python/assets/example_asset.txt +1 -0
- package/skills/using-asyncio-python/evals/evals.json +43 -0
- package/skills/using-asyncio-python/evals/results.json +13 -0
- package/skills/using-asyncio-python/examples/after.md +68 -0
- package/skills/using-asyncio-python/examples/before.md +39 -0
- package/skills/using-asyncio-python/references/api_reference.md +267 -0
- package/skills/using-asyncio-python/references/review-checklist.md +149 -0
- package/skills/using-asyncio-python/scripts/check_blocking.py +270 -0
- package/skills/using-asyncio-python/scripts/example.py +1 -0
- package/skills/web-scraping-python/SKILL.md +280 -0
- package/skills/web-scraping-python/assets/example_asset.txt +1 -0
- package/skills/web-scraping-python/evals/evals.json +46 -0
- package/skills/web-scraping-python/evals/results.json +13 -0
- package/skills/web-scraping-python/examples/after.md +109 -0
- package/skills/web-scraping-python/examples/before.md +40 -0
- package/skills/web-scraping-python/references/api_reference.md +393 -0
- package/skills/web-scraping-python/references/review-checklist.md +163 -0
- package/skills/web-scraping-python/scripts/example.py +1 -0
- package/skills/web-scraping-python/scripts/new_scraper.py +231 -0
- package/skills/writing-plans/audit.json +34 -0
- package/tests/agent-detector.test.js +83 -0
- package/tests/corrections.test.js +245 -0
- package/tests/doctor/hook-installer.test.js +72 -0
- package/tests/doctor/usage-tracker.test.js +140 -0
- package/tests/engine/benchmark-eval.test.js +31 -0
- package/tests/engine/bm25-index.test.js +85 -0
- package/tests/engine/capture-command.test.js +35 -0
- package/tests/engine/capture.test.js +17 -0
- package/tests/engine/graph-augmented-search.test.js +107 -0
- package/tests/engine/graph-injector.test.js +44 -0
- package/tests/engine/graph.test.js +216 -0
- package/tests/engine/hybrid-searcher.test.js +74 -0
- package/tests/engine/indexer-bm25.test.js +37 -0
- package/tests/engine/mcp-tools.test.js +73 -0
- package/tests/engine/project-initializer-mcp.test.js +99 -0
- package/tests/engine/query-expander.test.js +36 -0
- package/tests/engine/reranker.test.js +51 -0
- package/tests/engine/rrf.test.js +49 -0
- package/tests/engine/srag-prefix.test.js +47 -0
- package/tests/instinct-block.test.js +23 -0
- package/tests/mcp-config-writer.test.js +60 -0
- package/tests/project-initializer-new-agents.test.js +48 -0
- package/tests/rules/rules-manager.test.js +230 -0
- package/tests/well-known-builder.test.js +40 -0
- package/tests/wizard/integration-detector.test.js +31 -0
- package/tests/wizard/project-detector.test.js +51 -0
- package/tests/wizard/prompt-session.test.js +61 -0
- package/tests/wizard/prompt.test.js +16 -0
- package/tests/wizard/registry-embeddings.test.js +35 -0
- package/tests/wizard/skill-recommender.test.js +34 -0
- package/tests/wizard/slot-count.test.js +25 -0
- package/vercel.json +21 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# Programming with Rust — Practices Catalog
|
|
2
|
+
|
|
3
|
+
Deep before/after examples for the most critical Rust idioms from each chapter group.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Ownership: Borrow Instead of Clone (Ch 8)
|
|
8
|
+
|
|
9
|
+
**Before:**
|
|
10
|
+
```rust
|
|
11
|
+
fn print_names(names: Vec<String>) { // consumes the Vec
|
|
12
|
+
for name in names {
|
|
13
|
+
println!("{name}");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// names is gone after this call
|
|
17
|
+
```
|
|
18
|
+
**After:**
|
|
19
|
+
```rust
|
|
20
|
+
fn print_names(names: &[String]) { // borrows a slice — Vec<String> derefs to &[String]
|
|
21
|
+
for name in names {
|
|
22
|
+
println!("{name}");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// caller keeps ownership
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Ownership: Move Semantics (Ch 8)
|
|
31
|
+
|
|
32
|
+
**Before:**
|
|
33
|
+
```rust
|
|
34
|
+
let s1 = String::from("hello");
|
|
35
|
+
let s2 = s1; // s1 is MOVED into s2
|
|
36
|
+
println!("{s1}"); // compile error: s1 was moved
|
|
37
|
+
```
|
|
38
|
+
**After:**
|
|
39
|
+
```rust
|
|
40
|
+
let s1 = String::from("hello");
|
|
41
|
+
let s2 = s1.clone(); // explicit deep copy — both valid
|
|
42
|
+
println!("{s1} {s2}"); // both valid
|
|
43
|
+
|
|
44
|
+
// Or: borrow instead of cloning
|
|
45
|
+
let s3 = &s1; // borrow — s1 still owns the data
|
|
46
|
+
println!("{s1} {s3}");
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## References: Borrow Rules (Ch 10)
|
|
52
|
+
|
|
53
|
+
```rust
|
|
54
|
+
let mut data = vec![1, 2, 3];
|
|
55
|
+
|
|
56
|
+
// OK: multiple immutable borrows
|
|
57
|
+
let a = &data;
|
|
58
|
+
let b = &data;
|
|
59
|
+
println!("{a:?} {b:?}");
|
|
60
|
+
|
|
61
|
+
// OK: one mutable borrow (after immutable borrows end)
|
|
62
|
+
let c = &mut data;
|
|
63
|
+
c.push(4);
|
|
64
|
+
|
|
65
|
+
// COMPILE ERROR: can't have mutable + immutable borrow at same time
|
|
66
|
+
// let d = &data;
|
|
67
|
+
// let e = &mut data; // error
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Lifetimes: Elision vs Annotation (Ch 9)
|
|
73
|
+
|
|
74
|
+
**Elision works (no annotation needed):**
|
|
75
|
+
```rust
|
|
76
|
+
fn first_word(s: &str) -> &str { // compiler infers output lifetime = input lifetime
|
|
77
|
+
s.split_whitespace().next().unwrap_or("")
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Annotation required (multiple inputs, ambiguous output):**
|
|
82
|
+
```rust
|
|
83
|
+
// Without annotation — compiler can't tell which input the output borrows from
|
|
84
|
+
fn longer<'a>(s1: &'a str, s2: &'a str) -> &'a str {
|
|
85
|
+
if s1.len() > s2.len() { s1 } else { s2 }
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Struct holding a reference — must annotate:**
|
|
90
|
+
```rust
|
|
91
|
+
struct Excerpt<'a> {
|
|
92
|
+
text: &'a str, // struct can't outlive the string it borrows
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Error Handling: Result + ? (Ch 12)
|
|
99
|
+
|
|
100
|
+
**Before:**
|
|
101
|
+
```rust
|
|
102
|
+
fn read_username() -> String {
|
|
103
|
+
let f = std::fs::File::open("username.txt").unwrap();
|
|
104
|
+
let mut s = String::new();
|
|
105
|
+
std::io::Read::read_to_string(&mut std::io::BufReader::new(f), &mut s).unwrap();
|
|
106
|
+
s
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
**After:**
|
|
110
|
+
```rust
|
|
111
|
+
fn read_username() -> Result<String, std::io::Error> {
|
|
112
|
+
std::fs::read_to_string("username.txt") // ? propagates automatically
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Error Handling: Custom Error Types (Ch 12)
|
|
119
|
+
|
|
120
|
+
```rust
|
|
121
|
+
use std::fmt;
|
|
122
|
+
|
|
123
|
+
#[derive(Debug)]
|
|
124
|
+
pub enum AppError {
|
|
125
|
+
Io(std::io::Error),
|
|
126
|
+
Parse(std::num::ParseIntError),
|
|
127
|
+
Custom(String),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
impl fmt::Display for AppError {
|
|
131
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
132
|
+
match self {
|
|
133
|
+
AppError::Io(e) => write!(f, "I/O: {e}"),
|
|
134
|
+
AppError::Parse(e) => write!(f, "parse: {e}"),
|
|
135
|
+
AppError::Custom(s) => write!(f, "{s}"),
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
impl std::error::Error for AppError {}
|
|
141
|
+
|
|
142
|
+
// Enables ? to convert std::io::Error → AppError automatically
|
|
143
|
+
impl From<std::io::Error> for AppError {
|
|
144
|
+
fn from(e: std::io::Error) -> Self { AppError::Io(e) }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
impl From<std::num::ParseIntError> for AppError {
|
|
148
|
+
fn from(e: std::num::ParseIntError) -> Self { AppError::Parse(e) }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
fn parse_port(s: &str) -> Result<u16, AppError> {
|
|
152
|
+
let port: i32 = s.parse()?; // ParseIntError → AppError via From
|
|
153
|
+
if port < 1 || port > 65535 {
|
|
154
|
+
return Err(AppError::Custom(format!("invalid port: {port}")));
|
|
155
|
+
}
|
|
156
|
+
Ok(port as u16)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Traits: Static vs Dynamic Dispatch (Ch 17)
|
|
163
|
+
|
|
164
|
+
**Static dispatch — zero cost, preferred:**
|
|
165
|
+
```rust
|
|
166
|
+
// Monomorphized at compile time — no runtime overhead
|
|
167
|
+
fn notify(item: &impl Summary) {
|
|
168
|
+
println!("Breaking news! {}", item.summarize());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Equivalent with explicit generic:
|
|
172
|
+
fn notify<T: Summary>(item: &T) {
|
|
173
|
+
println!("Breaking news! {}", item.summarize());
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Dynamic dispatch — use only for heterogeneous collections or plugins:**
|
|
178
|
+
```rust
|
|
179
|
+
// Vtable lookup at runtime — small overhead, but enables mixed types
|
|
180
|
+
fn notify_all(items: &[Box<dyn Summary>]) {
|
|
181
|
+
for item in items {
|
|
182
|
+
println!("{}", item.summarize());
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Traits: Implementing Standard Traits (Ch 17)
|
|
190
|
+
|
|
191
|
+
```rust
|
|
192
|
+
use std::fmt;
|
|
193
|
+
|
|
194
|
+
#[derive(Debug, Clone, PartialEq)] // derive common traits
|
|
195
|
+
struct Point {
|
|
196
|
+
x: f64,
|
|
197
|
+
y: f64,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Display for user-facing output
|
|
201
|
+
impl fmt::Display for Point {
|
|
202
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
203
|
+
write!(f, "({}, {})", self.x, self.y)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// From for ergonomic conversion
|
|
208
|
+
impl From<(f64, f64)> for Point {
|
|
209
|
+
fn from((x, y): (f64, f64)) -> Self {
|
|
210
|
+
Point { x, y }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let p: Point = (1.0, 2.0).into(); // uses From impl
|
|
215
|
+
println!("{p}"); // uses Display
|
|
216
|
+
println!("{p:?}"); // uses Debug
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Pattern Matching: Exhaustive match (Ch 15)
|
|
222
|
+
|
|
223
|
+
**Before:**
|
|
224
|
+
```rust
|
|
225
|
+
let opt: Option<i32> = get_value();
|
|
226
|
+
match opt {
|
|
227
|
+
Some(x) => println!("{x}"),
|
|
228
|
+
_ => {} // silently ignores None — intent unclear
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
**After:**
|
|
232
|
+
```rust
|
|
233
|
+
// if let for single-variant match
|
|
234
|
+
if let Some(x) = get_value() {
|
|
235
|
+
println!("{x}");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Exhaustive match when both branches matter
|
|
239
|
+
match get_value() {
|
|
240
|
+
Some(x) => println!("got {x}"),
|
|
241
|
+
None => println!("nothing"),
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Closures: move for Threads (Ch 16, 18)
|
|
248
|
+
|
|
249
|
+
**Before:**
|
|
250
|
+
```rust
|
|
251
|
+
let name = String::from("Alice");
|
|
252
|
+
let handle = std::thread::spawn(|| {
|
|
253
|
+
println!("{name}"); // error: closure may outlive name
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
**After:**
|
|
257
|
+
```rust
|
|
258
|
+
let name = String::from("Alice");
|
|
259
|
+
let handle = std::thread::spawn(move || {
|
|
260
|
+
println!("{name}"); // name is moved into the closure — safe
|
|
261
|
+
});
|
|
262
|
+
handle.join().unwrap();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Concurrency: Arc<Mutex<T>> (Ch 19)
|
|
268
|
+
|
|
269
|
+
```rust
|
|
270
|
+
use std::sync::{Arc, Mutex};
|
|
271
|
+
use std::thread;
|
|
272
|
+
|
|
273
|
+
fn main() {
|
|
274
|
+
let counter = Arc::new(Mutex::new(0u32));
|
|
275
|
+
let mut handles = vec![];
|
|
276
|
+
|
|
277
|
+
for _ in 0..10 {
|
|
278
|
+
let counter = Arc::clone(&counter); // clone the Arc, not the data
|
|
279
|
+
let handle = thread::spawn(move || {
|
|
280
|
+
let mut num = counter.lock().expect("mutex poisoned");
|
|
281
|
+
*num += 1;
|
|
282
|
+
});
|
|
283
|
+
handles.push(handle);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
for handle in handles {
|
|
287
|
+
handle.join().unwrap();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
println!("Result: {}", *counter.lock().unwrap()); // 10
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Memory: RefCell for Interior Mutability (Ch 20)
|
|
297
|
+
|
|
298
|
+
Use `RefCell<T>` when you need mutability inside an otherwise immutable value (single-threaded only):
|
|
299
|
+
|
|
300
|
+
```rust
|
|
301
|
+
use std::cell::RefCell;
|
|
302
|
+
|
|
303
|
+
struct Cache {
|
|
304
|
+
data: RefCell<Option<String>>, // interior mutability
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
impl Cache {
|
|
308
|
+
fn get(&self) -> String {
|
|
309
|
+
// borrow_mut even though &self is immutable
|
|
310
|
+
let mut data = self.data.borrow_mut();
|
|
311
|
+
if data.is_none() {
|
|
312
|
+
*data = Some(expensive_computation());
|
|
313
|
+
}
|
|
314
|
+
data.as_ref().unwrap().clone()
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// For multi-threaded use, replace RefCell with Mutex
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Modules: Visibility (Ch 23)
|
|
323
|
+
|
|
324
|
+
```rust
|
|
325
|
+
// lib.rs
|
|
326
|
+
pub mod api { // public module
|
|
327
|
+
pub struct Request { /* ... */ } // public type
|
|
328
|
+
pub(crate) fn validate() { } // crate-internal only
|
|
329
|
+
fn internal_helper() { } // private to module
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
mod storage { // private module — not part of public API
|
|
333
|
+
pub(super) fn save() { } // accessible to parent module only
|
|
334
|
+
}
|
|
335
|
+
```
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
review.py — Pre-analysis script for Programming with Rust reviews.
|
|
4
|
+
Usage: python review.py <file.rs>
|
|
5
|
+
|
|
6
|
+
Scans a Rust source file for anti-patterns from the book:
|
|
7
|
+
unwrap misuse, unnecessary cloning, unsafe shared state, manual index loops,
|
|
8
|
+
missing Result return types, static mut, and more.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
CHECKS = [
|
|
17
|
+
(
|
|
18
|
+
r"\.unwrap\(\)",
|
|
19
|
+
"Ch 12: .unwrap()",
|
|
20
|
+
"panics on failure in production paths — use ?, .expect(\"reason\"), or match",
|
|
21
|
+
),
|
|
22
|
+
(
|
|
23
|
+
r"\.clone\(\)",
|
|
24
|
+
"Ch 8: .clone()",
|
|
25
|
+
"verify cloning is necessary — prefer borrowing (&T or &mut T) to avoid heap allocation",
|
|
26
|
+
),
|
|
27
|
+
(
|
|
28
|
+
r"static\s+mut\s+\w+",
|
|
29
|
+
"Ch 19: static mut",
|
|
30
|
+
"data race risk — replace with Arc<Mutex<T>> or std::sync::atomic types",
|
|
31
|
+
),
|
|
32
|
+
(
|
|
33
|
+
r"unsafe\s*\{",
|
|
34
|
+
"Ch 20: unsafe block",
|
|
35
|
+
"minimize unsafe scope; add a // SAFETY: comment explaining the invariant being upheld",
|
|
36
|
+
),
|
|
37
|
+
(
|
|
38
|
+
r"for\s+\w+\s+in\s+0\s*\.\.\s*\w+\.len\(\)",
|
|
39
|
+
"Ch 6: Manual index loop",
|
|
40
|
+
"use iterator adapters: for item in &collection, or .iter().enumerate() if index is needed",
|
|
41
|
+
),
|
|
42
|
+
(
|
|
43
|
+
r"\bpanic!\s*\(",
|
|
44
|
+
"Ch 12: panic!()",
|
|
45
|
+
"panics should be reserved for unrecoverable programmer errors — use Result<T, E> for recoverable failures",
|
|
46
|
+
),
|
|
47
|
+
(
|
|
48
|
+
r"Box<dyn\s+\w+>",
|
|
49
|
+
"Ch 17: dyn Trait (dynamic dispatch)",
|
|
50
|
+
"prefer impl Trait for static dispatch (zero-cost) unless you need a heterogeneous collection",
|
|
51
|
+
),
|
|
52
|
+
(
|
|
53
|
+
r"Rc\s*::\s*(new|clone)\b",
|
|
54
|
+
"Ch 19: Rc usage",
|
|
55
|
+
"Rc is not Send — if shared across threads, use Arc instead",
|
|
56
|
+
),
|
|
57
|
+
(
|
|
58
|
+
r"\.expect\s*\(\s*\)",
|
|
59
|
+
"Ch 12: .expect() with empty string",
|
|
60
|
+
"add a meaningful reason: .expect(\"invariant: config is always loaded before this point\")",
|
|
61
|
+
),
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def scan(source: str) -> list[dict]:
|
|
66
|
+
findings = []
|
|
67
|
+
lines = source.splitlines()
|
|
68
|
+
for lineno, line in enumerate(lines, start=1):
|
|
69
|
+
stripped = line.strip()
|
|
70
|
+
if stripped.startswith("//"):
|
|
71
|
+
continue
|
|
72
|
+
for pattern, label, advice in CHECKS:
|
|
73
|
+
if re.search(pattern, line):
|
|
74
|
+
findings.append({
|
|
75
|
+
"line": lineno,
|
|
76
|
+
"text": line.rstrip(),
|
|
77
|
+
"label": label,
|
|
78
|
+
"advice": advice,
|
|
79
|
+
})
|
|
80
|
+
return findings
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def sep(char="-", width=70) -> str:
|
|
84
|
+
return char * width
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def main() -> None:
|
|
88
|
+
if len(sys.argv) < 2:
|
|
89
|
+
print("Usage: python review.py <file.rs>")
|
|
90
|
+
sys.exit(1)
|
|
91
|
+
|
|
92
|
+
path = Path(sys.argv[1])
|
|
93
|
+
if not path.exists():
|
|
94
|
+
print(f"Error: file not found: {path}")
|
|
95
|
+
sys.exit(1)
|
|
96
|
+
|
|
97
|
+
if path.suffix.lower() != ".rs":
|
|
98
|
+
print(f"Warning: expected a .rs file, got '{path.suffix}' — continuing anyway")
|
|
99
|
+
|
|
100
|
+
source = path.read_text(encoding="utf-8", errors="replace")
|
|
101
|
+
findings = scan(source)
|
|
102
|
+
groups: dict[str, list] = {}
|
|
103
|
+
for f in findings:
|
|
104
|
+
groups.setdefault(f["label"], []).append(f)
|
|
105
|
+
|
|
106
|
+
print(sep("="))
|
|
107
|
+
print("PROGRAMMING WITH RUST — PRE-REVIEW REPORT")
|
|
108
|
+
print(sep("="))
|
|
109
|
+
print(f"File : {path}")
|
|
110
|
+
print(f"Lines : {len(source.splitlines())}")
|
|
111
|
+
print(f"Issues : {len(findings)} potential anti-patterns across {len(groups)} categories")
|
|
112
|
+
print()
|
|
113
|
+
|
|
114
|
+
if not findings:
|
|
115
|
+
print(" [OK] No common Rust anti-patterns detected.")
|
|
116
|
+
print()
|
|
117
|
+
else:
|
|
118
|
+
for label, items in groups.items():
|
|
119
|
+
print(sep())
|
|
120
|
+
print(f" {label} ({len(items)} occurrence{'s' if len(items) != 1 else ''})")
|
|
121
|
+
print(sep())
|
|
122
|
+
print(f" Advice: {items[0]['advice']}")
|
|
123
|
+
print()
|
|
124
|
+
for item in items[:5]:
|
|
125
|
+
print(f" line {item['line']:>4}: {item['text'][:100]}")
|
|
126
|
+
if len(items) > 5:
|
|
127
|
+
print(f" ... and {len(items) - 5} more")
|
|
128
|
+
print()
|
|
129
|
+
|
|
130
|
+
severity = (
|
|
131
|
+
"HIGH" if len(findings) >= 5
|
|
132
|
+
else "MEDIUM" if len(findings) >= 2
|
|
133
|
+
else "LOW" if findings
|
|
134
|
+
else "NONE"
|
|
135
|
+
)
|
|
136
|
+
print(sep("="))
|
|
137
|
+
print(f"SEVERITY: {severity} | Key chapters: Ch 8 (ownership), Ch 12 (errors), Ch 17 (traits), Ch 19 (concurrency), Ch 20 (memory)")
|
|
138
|
+
print(sep("="))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
if __name__ == "__main__":
|
|
142
|
+
main()
|