agy-superpowers 5.1.4 → 5.1.6
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/package.json +1 -1
- package/template/agent/rules/debug-confirmation-policy.md +34 -0
- package/template/agent/rules/language-matching.md +32 -0
- package/template/agent/skills/rust-developer/SKILL.md +281 -0
- package/template/agent/skills/rust-developer/references/rust-rules/_sections.md +231 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-clone-excessive.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-collect-intermediate.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-empty-catch.md +132 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-expect-lazy.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-format-hot-path.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-index-over-iter.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-lock-across-await.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-over-abstraction.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-panic-expected.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-premature-optimize.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-string-for-str.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-stringly-typed.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-type-erasure.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-unwrap-abuse.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/anti-vec-for-slice.md +121 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-must-use.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-builder-pattern.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-common-traits.md +165 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-default-impl.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-extension-trait.md +163 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-from-not-into.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-asref.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-impl-into.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-must-use.md +125 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-newtype-safety.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-non-exhaustive.md +177 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-parse-dont-validate.md +184 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-sealed-trait.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-serde-optional.md +182 -0
- package/template/agent/skills/rust-developer/references/rust-rules/api-typestate.md +199 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-bounded-channel.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-broadcast-pubsub.md +185 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-cancellation-token.md +203 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-clone-before-await.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-join-parallel.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-joinset-structured.md +195 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-mpsc-queue.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-no-lock-await.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-oneshot-response.md +191 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-select-racing.md +198 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-spawn-blocking.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-fs.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-tokio-runtime.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-try-join.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/async-watch-latest.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-all-public.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-cargo-metadata.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-errors-section.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-examples-section.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-hidden-setup.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-intra-links.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-link-types.md +169 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-module-inner.md +116 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-panics-section.md +128 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-question-mark.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/doc-safety-section.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-anyhow-app.md +179 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-context-chain.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-custom-type.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-doc-errors.md +145 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-expect-bugs-only.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-from-impl.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-lowercase-msg.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-no-unwrap-prod.md +115 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-question-mark.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-result-over-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-source-chain.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/err-thiserror-lib.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-cargo-metadata.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-deny-correctness.md +107 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-missing-docs.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-pedantic-selective.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-rustfmt-check.md +157 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-unsafe-doc.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-complexity.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-perf.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-style.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-warn-suspicious.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/lint-workspace-lints.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arena-allocator.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-arrayvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-assert-type-size.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-avoid-format.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-box-large-variant.md +158 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-boxed-slice.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-clone-from.md +147 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-compact-string.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-reuse-collections.md +174 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smaller-integers.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-smallvec.md +138 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-thinvec.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-with-capacity.md +156 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-write-over-format.md +172 -0
- package/template/agent/skills/rust-developer/references/rust-rules/mem-zero-copy.md +164 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-acronym-word.md +99 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-as-free.md +104 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-consts-screaming.md +94 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-crate-no-rs.md +78 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-funcs-snake.md +76 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-into-ownership.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-is-has-bool.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-convention.md +129 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-method.md +131 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-iter-type-match.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-lifetime-short.md +86 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-no-get-prefix.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-to-expensive.md +118 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-type-param-single.md +92 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-types-camel.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/name-variants-camel.md +101 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-bounds-check.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cache-friendly.md +187 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-codegen-units.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-cold-unlikely.md +152 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-always-rare.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-never-cold.md +181 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-inline-small.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-likely-hint.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-lto-release.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-pgo-profile.md +167 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-simd-portable.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/opt-target-cpu.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-arc-shared.md +141 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-borrow-over-clone.md +95 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-clone-explicit.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-copy-small.md +124 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-cow-conditional.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-lifetime-elision.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-move-large.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-mutex-interior.md +105 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rc-single-thread.md +65 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-refcell-interior.md +97 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-rwlock-readers.md +122 -0
- package/template/agent/skills/rust-developer/references/rust-rules/own-slice-over-vec.md +119 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-black-box-bench.md +153 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-chain-avoid.md +136 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-into.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-collect-once.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-drain-reuse.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-entry-api.md +134 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-extend-batch.md +150 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-lazy.md +123 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-iter-over-index.md +113 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-profile-first.md +175 -0
- package/template/agent/skills/rust-developer/references/rust-rules/perf-release-profile.md +149 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-bin-dir.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-flat-small.md +133 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-lib-main-split.md +148 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-by-feature.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-mod-rs-dir.md +120 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-prelude-module.md +155 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-crate-internal.md +139 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-super-parent.md +135 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-pub-use-reexport.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-deps.md +186 -0
- package/template/agent/skills/rust-developer/references/rust-rules/proj-workspace-large.md +162 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-arrange-act-assert.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-cfg-test-module.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-criterion-bench.md +171 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-descriptive-names.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-doctest-examples.md +168 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-fixture-raii.md +151 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-integration-dir.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mock-traits.md +189 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-mockall-mocking.md +226 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-proptest-properties.md +161 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-should-panic.md +130 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-tokio-async.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/test-use-super.md +127 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-enum-states.md +154 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-generic-bounds.md +142 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-never-diverge.md +146 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-ids.md +160 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-newtype-validated.md +159 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-no-stringly.md +144 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-option-nullable.md +137 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-phantom-marker.md +188 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-repr-transparent.md +143 -0
- package/template/agent/skills/rust-developer/references/rust-rules/type-result-fallible.md +131 -0
- package/template/agent/skills/systematic-debugging/SKILL.md +17 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# api-extension-trait
|
|
2
|
+
|
|
3
|
+
> Use extension traits to add methods to external types
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
Rust's orphan rules prevent implementing external traits on external types. Extension traits provide a workaround: define a new trait with your methods, then implement it for the external type. This pattern is used extensively in the ecosystem (e.g., `itertools::Itertools`, `tokio::AsyncReadExt`).
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Can't add methods directly to external types
|
|
13
|
+
impl Vec<u8> {
|
|
14
|
+
fn as_hex(&self) -> String {
|
|
15
|
+
// Error: cannot define inherent impl for a type outside this crate
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Can't implement external trait for external type
|
|
20
|
+
impl SomeExternalTrait for Vec<u8> {
|
|
21
|
+
// Error: orphan rules violation
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Good
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
// Define an extension trait
|
|
29
|
+
pub trait ByteSliceExt {
|
|
30
|
+
fn as_hex(&self) -> String;
|
|
31
|
+
fn is_ascii_printable(&self) -> bool;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Implement for the external type
|
|
35
|
+
impl ByteSliceExt for [u8] {
|
|
36
|
+
fn as_hex(&self) -> String {
|
|
37
|
+
self.iter()
|
|
38
|
+
.map(|b| format!("{:02x}", b))
|
|
39
|
+
.collect()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
fn is_ascii_printable(&self) -> bool {
|
|
43
|
+
self.iter().all(|b| b.is_ascii_graphic() || b.is_ascii_whitespace())
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Usage: import the trait to use the methods
|
|
48
|
+
use my_crate::ByteSliceExt;
|
|
49
|
+
|
|
50
|
+
let data: &[u8] = b"hello";
|
|
51
|
+
println!("{}", data.as_hex()); // "68656c6c6f"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Convention: Ext Suffix
|
|
55
|
+
|
|
56
|
+
```rust
|
|
57
|
+
// Standard naming: TypeExt for extending Type
|
|
58
|
+
pub trait OptionExt<T> {
|
|
59
|
+
fn unwrap_or_log(self, msg: &str) -> Option<T>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
impl<T> OptionExt<T> for Option<T> {
|
|
63
|
+
fn unwrap_or_log(self, msg: &str) -> Option<T> {
|
|
64
|
+
if self.is_none() {
|
|
65
|
+
log::warn!("{}", msg);
|
|
66
|
+
}
|
|
67
|
+
self
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// For generic extensions
|
|
72
|
+
pub trait ResultExt<T, E> {
|
|
73
|
+
fn log_err(self) -> Self;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
impl<T, E: std::fmt::Display> ResultExt<T, E> for Result<T, E> {
|
|
77
|
+
fn log_err(self) -> Self {
|
|
78
|
+
if let Err(ref e) = self {
|
|
79
|
+
log::error!("{}", e);
|
|
80
|
+
}
|
|
81
|
+
self
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Ecosystem Examples
|
|
87
|
+
|
|
88
|
+
```rust
|
|
89
|
+
// itertools::Itertools
|
|
90
|
+
use itertools::Itertools;
|
|
91
|
+
let groups = vec![1, 1, 2, 2, 3].into_iter().group_by(|x| *x);
|
|
92
|
+
|
|
93
|
+
// futures::StreamExt
|
|
94
|
+
use futures::StreamExt;
|
|
95
|
+
let next = stream.next().await;
|
|
96
|
+
|
|
97
|
+
// tokio::io::AsyncReadExt
|
|
98
|
+
use tokio::io::AsyncReadExt;
|
|
99
|
+
let mut buf = [0u8; 1024];
|
|
100
|
+
reader.read(&mut buf).await?;
|
|
101
|
+
|
|
102
|
+
// anyhow::Context
|
|
103
|
+
use anyhow::Context;
|
|
104
|
+
let content = std::fs::read_to_string(path)
|
|
105
|
+
.with_context(|| format!("failed to read {}", path))?;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Scoped Extensions
|
|
109
|
+
|
|
110
|
+
```rust
|
|
111
|
+
// Extension only visible where imported
|
|
112
|
+
mod string_utils {
|
|
113
|
+
pub trait StringExt {
|
|
114
|
+
fn truncate_ellipsis(&self, max_len: usize) -> String;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
impl StringExt for str {
|
|
118
|
+
fn truncate_ellipsis(&self, max_len: usize) -> String {
|
|
119
|
+
if self.len() <= max_len {
|
|
120
|
+
self.to_string()
|
|
121
|
+
} else {
|
|
122
|
+
format!("{}...", &self[..max_len.saturating_sub(3)])
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Only available when explicitly imported
|
|
129
|
+
use string_utils::StringExt;
|
|
130
|
+
let short = "very long string".truncate_ellipsis(10);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Generic Extensions with Bounds
|
|
134
|
+
|
|
135
|
+
```rust
|
|
136
|
+
pub trait VecExt<T> {
|
|
137
|
+
fn push_if_unique(&mut self, item: T)
|
|
138
|
+
where
|
|
139
|
+
T: PartialEq;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
impl<T> VecExt<T> for Vec<T> {
|
|
143
|
+
fn push_if_unique(&mut self, item: T)
|
|
144
|
+
where
|
|
145
|
+
T: PartialEq,
|
|
146
|
+
{
|
|
147
|
+
if !self.contains(&item) {
|
|
148
|
+
self.push(item);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Works with any T: PartialEq
|
|
154
|
+
let mut v = vec![1, 2, 3];
|
|
155
|
+
v.push_if_unique(2); // No-op
|
|
156
|
+
v.push_if_unique(4); // Adds 4
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## See Also
|
|
160
|
+
|
|
161
|
+
- [api-sealed-trait](./api-sealed-trait.md) - Controlling trait implementations
|
|
162
|
+
- [api-impl-into](./api-impl-into.md) - Using standard conversion traits
|
|
163
|
+
- [name-as-free](./name-as-free.md) - Naming conventions for conversions
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# api-from-not-into
|
|
2
|
+
|
|
3
|
+
> Implement `From<T>`, not `Into<U>` - From gives you Into for free
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
The standard library has a blanket implementation: `impl<T, U> Into<U> for T where U: From<T>`. This means implementing `From<T> for U` automatically gives you `Into<U> for T`. Implementing `Into` directly bypasses this and is considered non-idiomatic. Always implement `From`.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
struct UserId(u64);
|
|
13
|
+
|
|
14
|
+
// Non-idiomatic: implementing Into directly
|
|
15
|
+
impl Into<UserId> for u64 {
|
|
16
|
+
fn into(self) -> UserId {
|
|
17
|
+
UserId(self)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Works, but now you can't use From syntax
|
|
22
|
+
let id = UserId::from(42); // Error: From not implemented
|
|
23
|
+
let id: UserId = 42.into(); // Works, but limited
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Good
|
|
27
|
+
|
|
28
|
+
```rust
|
|
29
|
+
struct UserId(u64);
|
|
30
|
+
|
|
31
|
+
// Idiomatic: implement From
|
|
32
|
+
impl From<u64> for UserId {
|
|
33
|
+
fn from(id: u64) -> Self {
|
|
34
|
+
UserId(id)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Now both work automatically
|
|
39
|
+
let id = UserId::from(42); // From syntax
|
|
40
|
+
let id: UserId = 42.into(); // Into syntax (via blanket impl)
|
|
41
|
+
|
|
42
|
+
// And Into bound works in generics
|
|
43
|
+
fn process(id: impl Into<UserId>) {
|
|
44
|
+
let id: UserId = id.into();
|
|
45
|
+
}
|
|
46
|
+
process(42u64); // Works!
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Blanket Implementation
|
|
50
|
+
|
|
51
|
+
```rust
|
|
52
|
+
// This is in std, you don't write it
|
|
53
|
+
impl<T, U> Into<U> for T
|
|
54
|
+
where
|
|
55
|
+
U: From<T>,
|
|
56
|
+
{
|
|
57
|
+
fn into(self) -> U {
|
|
58
|
+
U::from(self)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// So when you implement From:
|
|
63
|
+
impl From<String> for MyType { ... }
|
|
64
|
+
|
|
65
|
+
// You automatically get:
|
|
66
|
+
// impl Into<MyType> for String { ... }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Multiple From Implementations
|
|
70
|
+
|
|
71
|
+
```rust
|
|
72
|
+
struct Email(String);
|
|
73
|
+
|
|
74
|
+
impl From<String> for Email {
|
|
75
|
+
fn from(s: String) -> Self {
|
|
76
|
+
Email(s)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
impl From<&str> for Email {
|
|
81
|
+
fn from(s: &str) -> Self {
|
|
82
|
+
Email(s.to_string())
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// All of these work
|
|
87
|
+
let e1 = Email::from("test@example.com");
|
|
88
|
+
let e2 = Email::from(String::from("test@example.com"));
|
|
89
|
+
let e3: Email = "test@example.com".into();
|
|
90
|
+
let e4: Email = String::from("test@example.com").into();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## TryFrom for Fallible Conversions
|
|
94
|
+
|
|
95
|
+
```rust
|
|
96
|
+
use std::convert::TryFrom;
|
|
97
|
+
|
|
98
|
+
struct PositiveInt(u32);
|
|
99
|
+
|
|
100
|
+
// Fallible conversion
|
|
101
|
+
impl TryFrom<i32> for PositiveInt {
|
|
102
|
+
type Error = &'static str;
|
|
103
|
+
|
|
104
|
+
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
|
105
|
+
if value > 0 {
|
|
106
|
+
Ok(PositiveInt(value as u32))
|
|
107
|
+
} else {
|
|
108
|
+
Err("value must be positive")
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Usage
|
|
114
|
+
let pos = PositiveInt::try_from(42)?; // From-style
|
|
115
|
+
let pos: PositiveInt = 42.try_into()?; // Into-style (via blanket)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Clippy Lint
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
[lints.clippy]
|
|
122
|
+
from_over_into = "warn" # Warns when implementing Into instead of From
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```rust
|
|
126
|
+
// Clippy will warn:
|
|
127
|
+
impl Into<Bar> for Foo { // Warning: prefer From
|
|
128
|
+
fn into(self) -> Bar { ... }
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## When Into IS Needed (Rare)
|
|
133
|
+
|
|
134
|
+
```rust
|
|
135
|
+
// Only when implementing for external types in specific trait bounds
|
|
136
|
+
// This is very rare and usually indicates a design issue
|
|
137
|
+
|
|
138
|
+
// Example: you can't implement From<ExternalA> for ExternalB
|
|
139
|
+
// because of orphan rules. But you usually shouldn't need to.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## See Also
|
|
143
|
+
|
|
144
|
+
- [api-impl-into](./api-impl-into.md) - Using Into in function parameters
|
|
145
|
+
- [err-from-impl](./err-from-impl.md) - From for error types
|
|
146
|
+
- [api-newtype-safety](./api-newtype-safety.md) - Newtype conversions
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# api-impl-asref
|
|
2
|
+
|
|
3
|
+
> Use `AsRef<T>` when you only need to borrow the inner data
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
`AsRef<T>` provides a cheap borrowed view of data without taking ownership or copying. Functions accepting `impl AsRef<T>` can work with multiple types that contain or represent `T`, making APIs flexible while avoiding unnecessary allocations. Use `AsRef` when you only need to read, `Into` when you need to own.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Forces callers to provide exact types
|
|
13
|
+
fn process_text(text: &str) { ... }
|
|
14
|
+
fn read_file(path: &Path) { ... }
|
|
15
|
+
|
|
16
|
+
// Can't call directly with owned types
|
|
17
|
+
let s = String::from("hello");
|
|
18
|
+
process_text(&s); // Works but verbose
|
|
19
|
+
|
|
20
|
+
let p = PathBuf::from("/file");
|
|
21
|
+
read_file(&p); // Works but verbose
|
|
22
|
+
read_file("/file"); // Error! &str != &Path
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Good
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
// Accept anything that can be viewed as the target type
|
|
29
|
+
fn process_text(text: impl AsRef<str>) {
|
|
30
|
+
let s: &str = text.as_ref();
|
|
31
|
+
println!("{}", s);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn read_file(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
|
|
35
|
+
std::fs::read(path.as_ref())
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// All of these work:
|
|
39
|
+
process_text("literal"); // &str
|
|
40
|
+
process_text(String::from("owned")); // String
|
|
41
|
+
process_text(Cow::from("cow")); // Cow<str>
|
|
42
|
+
|
|
43
|
+
read_file("/path/to/file"); // &str
|
|
44
|
+
read_file(Path::new("/path")); // &Path
|
|
45
|
+
read_file(PathBuf::from("/path")); // PathBuf
|
|
46
|
+
read_file(OsStr::new("/path")); // &OsStr
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## AsRef vs Into vs Borrow
|
|
50
|
+
|
|
51
|
+
```rust
|
|
52
|
+
// AsRef<T>: cheap borrow, no ownership transfer
|
|
53
|
+
fn read(p: impl AsRef<Path>) {
|
|
54
|
+
let path: &Path = p.as_ref();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Into<T>: ownership transfer, may allocate
|
|
58
|
+
fn store(p: impl Into<PathBuf>) {
|
|
59
|
+
let owned: PathBuf = p.into();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Borrow<T>: like AsRef but with Eq/Hash consistency guarantee
|
|
63
|
+
use std::borrow::Borrow;
|
|
64
|
+
fn lookup<Q: ?Sized>(map: &HashMap<String, V>, key: &Q) -> Option<&V>
|
|
65
|
+
where
|
|
66
|
+
String: Borrow<Q>,
|
|
67
|
+
Q: Hash + Eq,
|
|
68
|
+
{
|
|
69
|
+
map.get(key)
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Implement AsRef for Custom Types
|
|
74
|
+
|
|
75
|
+
```rust
|
|
76
|
+
struct Name(String);
|
|
77
|
+
|
|
78
|
+
impl AsRef<str> for Name {
|
|
79
|
+
fn as_ref(&self) -> &str {
|
|
80
|
+
&self.0
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
impl AsRef<[u8]> for Name {
|
|
85
|
+
fn as_ref(&self) -> &[u8] {
|
|
86
|
+
self.0.as_bytes()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Now Name works with functions expecting AsRef<str>
|
|
91
|
+
fn greet(name: impl AsRef<str>) {
|
|
92
|
+
println!("Hello, {}!", name.as_ref());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
greet(Name("Alice".into()));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Common AsRef Implementations
|
|
99
|
+
|
|
100
|
+
```rust
|
|
101
|
+
// Standard library provides many
|
|
102
|
+
impl AsRef<str> for String { ... }
|
|
103
|
+
impl AsRef<str> for str { ... }
|
|
104
|
+
impl AsRef<[u8]> for str { ... }
|
|
105
|
+
impl AsRef<[u8]> for String { ... }
|
|
106
|
+
impl AsRef<[u8]> for Vec<u8> { ... }
|
|
107
|
+
impl AsRef<Path> for str { ... }
|
|
108
|
+
impl AsRef<Path> for String { ... }
|
|
109
|
+
impl AsRef<Path> for PathBuf { ... }
|
|
110
|
+
impl AsRef<Path> for OsStr { ... }
|
|
111
|
+
impl AsRef<OsStr> for str { ... }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## When to Use Which
|
|
115
|
+
|
|
116
|
+
| Trait | Use When |
|
|
117
|
+
|-------|----------|
|
|
118
|
+
| `&T` | Single type, simple API |
|
|
119
|
+
| `AsRef<T>` | Read-only access, multiple input types |
|
|
120
|
+
| `Into<T>` | Need to store/own the value |
|
|
121
|
+
| `Borrow<T>` | HashMap/HashSet keys, Eq/Hash needed |
|
|
122
|
+
| `Deref<Target=T>` | Smart pointer semantics |
|
|
123
|
+
|
|
124
|
+
## Pattern: Optional AsRef Bound
|
|
125
|
+
|
|
126
|
+
```rust
|
|
127
|
+
// When T itself might be passed
|
|
128
|
+
fn process<T: AsRef<U>, U>(value: T) {
|
|
129
|
+
let inner: &U = value.as_ref();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// More flexible: accept T or &T
|
|
133
|
+
fn process<T: AsRef<U> + ?Sized, U: ?Sized>(value: &T) {
|
|
134
|
+
let inner: &U = value.as_ref();
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## See Also
|
|
139
|
+
|
|
140
|
+
- [api-impl-into](./api-impl-into.md) - When to use Into instead
|
|
141
|
+
- [own-slice-over-vec](./own-slice-over-vec.md) - Using slices for flexibility
|
|
142
|
+
- [own-borrow-over-clone](./own-borrow-over-clone.md) - Preferring borrows
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# api-impl-into
|
|
2
|
+
|
|
3
|
+
> Accept `impl Into<T>` for flexible APIs, implement `From<T>` for conversions
|
|
4
|
+
|
|
5
|
+
## Why It Matters
|
|
6
|
+
|
|
7
|
+
APIs that accept `impl Into<T>` are ergonomic—callers can pass the target type directly or any type that converts to it. This reduces boilerplate `.into()` calls at call sites. Implement `From<T>` rather than `Into<T>` because `From` implies `Into` through a blanket implementation.
|
|
8
|
+
|
|
9
|
+
## Bad
|
|
10
|
+
|
|
11
|
+
```rust
|
|
12
|
+
// Requires exact type - forces callers to convert
|
|
13
|
+
fn process_path(path: PathBuf) { ... }
|
|
14
|
+
fn set_name(name: String) { ... }
|
|
15
|
+
|
|
16
|
+
// Caller must convert explicitly
|
|
17
|
+
process_path(PathBuf::from("/path/to/file"));
|
|
18
|
+
process_path("/path/to/file".to_path_buf()); // Verbose
|
|
19
|
+
process_path("/path/to/file".into()); // Explicit
|
|
20
|
+
|
|
21
|
+
set_name(String::from("Alice"));
|
|
22
|
+
set_name("Alice".to_string()); // Verbose
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Good
|
|
26
|
+
|
|
27
|
+
```rust
|
|
28
|
+
// Accept anything that converts to the target type
|
|
29
|
+
fn process_path(path: impl Into<PathBuf>) {
|
|
30
|
+
let path = path.into(); // Convert once inside
|
|
31
|
+
// ...
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn set_name(name: impl Into<String>) {
|
|
35
|
+
let name = name.into();
|
|
36
|
+
// ...
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Callers are ergonomic
|
|
40
|
+
process_path("/path/to/file"); // &str converts automatically
|
|
41
|
+
process_path(PathBuf::from(".")); // PathBuf works too
|
|
42
|
+
|
|
43
|
+
set_name("Alice"); // &str
|
|
44
|
+
set_name(String::from("Alice")); // String
|
|
45
|
+
set_name(format!("User-{}", id)); // String from format!
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Implement From, Not Into
|
|
49
|
+
|
|
50
|
+
```rust
|
|
51
|
+
struct UserId(u64);
|
|
52
|
+
|
|
53
|
+
// ✅ Implement From
|
|
54
|
+
impl From<u64> for UserId {
|
|
55
|
+
fn from(id: u64) -> Self {
|
|
56
|
+
UserId(id)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Into is automatically provided by blanket impl
|
|
61
|
+
let id: UserId = 42u64.into(); // Works!
|
|
62
|
+
|
|
63
|
+
// ❌ Don't implement Into directly
|
|
64
|
+
impl Into<UserId> for u64 {
|
|
65
|
+
fn into(self) -> UserId {
|
|
66
|
+
UserId(self) // This works but is non-idiomatic
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Common Conversions
|
|
72
|
+
|
|
73
|
+
```rust
|
|
74
|
+
// String-like types
|
|
75
|
+
fn log_message(msg: impl Into<String>) { ... }
|
|
76
|
+
log_message("literal"); // &str
|
|
77
|
+
log_message(String::from("own")); // String
|
|
78
|
+
log_message(Cow::from("cow")); // Cow<str>
|
|
79
|
+
|
|
80
|
+
// Path-like types
|
|
81
|
+
fn read_file(path: impl AsRef<Path>) { ... } // AsRef for borrowed access
|
|
82
|
+
fn write_file(path: impl Into<PathBuf>) { ... } // Into when storing
|
|
83
|
+
|
|
84
|
+
// Duration
|
|
85
|
+
fn set_timeout(duration: impl Into<Duration>) { ... }
|
|
86
|
+
set_timeout(Duration::from_secs(5));
|
|
87
|
+
// Note: no blanket impl for integers, would need custom wrapper
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## AsRef vs Into
|
|
91
|
+
|
|
92
|
+
```rust
|
|
93
|
+
// AsRef<T>: borrow as &T, no conversion cost
|
|
94
|
+
fn count_bytes(data: impl AsRef<[u8]>) -> usize {
|
|
95
|
+
data.as_ref().len() // Just borrows, no allocation
|
|
96
|
+
}
|
|
97
|
+
count_bytes("hello"); // &str -> &[u8]
|
|
98
|
+
count_bytes(b"hello"); // &[u8] -> &[u8]
|
|
99
|
+
count_bytes(vec![1, 2, 3]); // &Vec<u8> -> &[u8]
|
|
100
|
+
|
|
101
|
+
// Into<T>: convert to owned T, may allocate
|
|
102
|
+
fn store_data(data: impl Into<Vec<u8>>) {
|
|
103
|
+
let owned: Vec<u8> = data.into(); // Takes ownership
|
|
104
|
+
// ...
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## When NOT to Use impl Into
|
|
109
|
+
|
|
110
|
+
```rust
|
|
111
|
+
// ❌ Trait objects need Sized
|
|
112
|
+
fn process(handler: impl Into<Box<dyn Handler>>) { }
|
|
113
|
+
// Better: just take Box<dyn Handler> directly
|
|
114
|
+
|
|
115
|
+
// ❌ Recursive types
|
|
116
|
+
struct Node {
|
|
117
|
+
children: Vec<impl Into<Node>>, // Error: impl Trait not allowed here
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ❌ Performance-critical hot paths (minor overhead of trait dispatch)
|
|
121
|
+
fn hot_path(value: impl Into<u64>) {
|
|
122
|
+
// Consider taking u64 directly if called billions of times
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ❌ When you need to name the type
|
|
126
|
+
fn returns_impl() -> impl Into<String> { } // Opaque, hard to use
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Builder Pattern with Into
|
|
130
|
+
|
|
131
|
+
```rust
|
|
132
|
+
struct Config {
|
|
133
|
+
name: String,
|
|
134
|
+
path: PathBuf,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
impl Config {
|
|
138
|
+
fn new(name: impl Into<String>) -> Self {
|
|
139
|
+
Config {
|
|
140
|
+
name: name.into(),
|
|
141
|
+
path: PathBuf::new(),
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
fn path(mut self, path: impl Into<PathBuf>) -> Self {
|
|
146
|
+
self.path = path.into();
|
|
147
|
+
self
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Clean builder calls
|
|
152
|
+
let config = Config::new("myapp")
|
|
153
|
+
.path("/etc/myapp");
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## See Also
|
|
157
|
+
|
|
158
|
+
- [api-impl-asref](./api-impl-asref.md) - When to use AsRef instead
|
|
159
|
+
- [api-from-not-into](./api-from-not-into.md) - Why From is preferred
|
|
160
|
+
- [err-from-impl](./err-from-impl.md) - From for error conversion
|