agentic-team-templates 0.10.0 → 0.12.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.
@@ -0,0 +1,256 @@
1
+ # Rust Performance and Unsafe
2
+
3
+ Rust's zero-cost abstractions mean you rarely need to choose between ergonomics and performance. When you do need to go lower, `unsafe` is the mechanism — and it comes with strict obligations.
4
+
5
+ ## Performance
6
+
7
+ ### Measure First
8
+
9
+ ```bash
10
+ # Profile before optimizing
11
+ cargo bench # Run benchmarks
12
+ cargo flamegraph # Generate flamegraph (needs cargo-flamegraph)
13
+ cargo instruments -t "Time Profiler" # macOS Instruments
14
+
15
+ # Check binary size
16
+ cargo bloat --release --crates # Which crates contribute to binary size
17
+ cargo bloat --release --filter "my_" # Your functions by size
18
+
19
+ # Check compile times
20
+ cargo build --timings # HTML report of compile times per crate
21
+ ```
22
+
23
+ ### Allocation Patterns
24
+
25
+ ```rust
26
+ // Preallocate collections when size is known
27
+ let mut results = Vec::with_capacity(items.len());
28
+ for item in items {
29
+ results.push(process(item));
30
+ }
31
+
32
+ // Use iterators — they often avoid allocation entirely
33
+ let sum: i64 = items.iter()
34
+ .filter(|x| x.is_valid())
35
+ .map(|x| x.value())
36
+ .sum();
37
+
38
+ // Avoid unnecessary clones
39
+ fn process(data: &str) -> Result<Output> { ... } // Borrow when possible
40
+ fn consume(data: String) -> Result<Output> { ... } // Take ownership when needed
41
+
42
+ // Cow for conditional ownership
43
+ use std::borrow::Cow;
44
+ fn normalize(input: &str) -> Cow<'_, str> {
45
+ if input.contains('\t') {
46
+ Cow::Owned(input.replace('\t', " "))
47
+ } else {
48
+ Cow::Borrowed(input) // Zero allocation when no tabs
49
+ }
50
+ }
51
+
52
+ // SmallVec for small, stack-allocated collections
53
+ use smallvec::SmallVec;
54
+ let mut tags: SmallVec<[Tag; 4]> = SmallVec::new();
55
+ // Up to 4 tags on the stack, spills to heap only if exceeded
56
+ ```
57
+
58
+ ### String Performance
59
+
60
+ ```rust
61
+ // String concatenation: use a builder pattern
62
+ use std::fmt::Write;
63
+ let mut output = String::with_capacity(estimated_size);
64
+ for item in items {
65
+ write!(output, "{}: {}\n", item.key, item.value).unwrap();
66
+ }
67
+
68
+ // For byte-level work, operate on &[u8] instead of &str
69
+ // Converting to str requires UTF-8 validation — skip it when you can
70
+
71
+ // Interning for repeated strings
72
+ // Use string interning crates (lasso, string_cache) for heavy deduplication
73
+ ```
74
+
75
+ ### Iterator Optimization
76
+
77
+ ```rust
78
+ // Iterators compile to the same code as manual loops — use them freely
79
+
80
+ // Collect into specific types
81
+ let map: HashMap<_, _> = pairs.into_iter().collect();
82
+ let set: HashSet<_> = items.into_iter().collect();
83
+
84
+ // Avoid collect() when you don't need a collection
85
+ // Bad: allocates a Vec just to iterate it
86
+ let filtered: Vec<_> = items.iter().filter(|x| x.active).collect();
87
+ for item in &filtered { ... }
88
+
89
+ // Good: iterate directly
90
+ for item in items.iter().filter(|x| x.active) { ... }
91
+
92
+ // chunks/windows for batch processing
93
+ for chunk in data.chunks(1024) {
94
+ process_batch(chunk)?;
95
+ }
96
+ ```
97
+
98
+ ### Data Layout
99
+
100
+ ```rust
101
+ // Field ordering affects struct size due to padding
102
+ // Bad: 24 bytes (with padding)
103
+ struct Padded {
104
+ a: u8, // 1 byte + 7 padding
105
+ b: u64, // 8 bytes
106
+ c: u8, // 1 byte + 7 padding
107
+ }
108
+
109
+ // Good: 16 bytes (reordered to minimize padding)
110
+ struct Compact {
111
+ b: u64, // 8 bytes
112
+ a: u8, // 1 byte
113
+ c: u8, // 1 byte + 6 padding
114
+ }
115
+
116
+ // repr(C) for FFI — disables Rust's field reordering
117
+ #[repr(C)]
118
+ struct FfiStruct { ... }
119
+
120
+ // The compiler may reorder fields automatically in default repr(Rust),
121
+ // but being explicit about layout helps readability and FFI correctness
122
+ ```
123
+
124
+ ## Unsafe
125
+
126
+ ### The Contract
127
+
128
+ Every `unsafe` block is a proof obligation. You are telling the compiler: "I have verified that the safety invariants hold here, and I accept responsibility."
129
+
130
+ ```rust
131
+ // ALWAYS document the safety invariant
132
+ // SAFETY: We've verified that `index` is within bounds via the
133
+ // length check on line 42, and the slice is valid for the lifetime
134
+ // of this function.
135
+ unsafe {
136
+ *ptr.add(index) = value;
137
+ }
138
+ ```
139
+
140
+ ### Valid Uses of Unsafe
141
+
142
+ ```rust
143
+ // 1. Calling unsafe functions (FFI)
144
+ extern "C" {
145
+ fn external_function(ptr: *const u8, len: usize) -> i32;
146
+ }
147
+
148
+ pub fn safe_wrapper(data: &[u8]) -> Result<i32> {
149
+ // SAFETY: data.as_ptr() is valid for data.len() bytes,
150
+ // and the external function only reads from the pointer.
151
+ let result = unsafe { external_function(data.as_ptr(), data.len()) };
152
+ if result < 0 {
153
+ Err(Error::ExternalFailure(result))
154
+ } else {
155
+ Ok(result)
156
+ }
157
+ }
158
+
159
+ // 2. Implementing unsafe traits
160
+ // SAFETY: MyType is Send because its internal raw pointer
161
+ // is only accessed from the thread that owns MyType.
162
+ // The pointer is never shared or aliased.
163
+ unsafe impl Send for MyType {}
164
+
165
+ // 3. Accessing mutable statics
166
+ static mut COUNTER: u64 = 0;
167
+ // SAFETY: This is only called from a single thread during initialization.
168
+ unsafe { COUNTER += 1; }
169
+ // Prefer AtomicU64 or OnceLock instead — this is almost always avoidable.
170
+
171
+ // 4. Unchecked operations for performance (after profiling proves it matters)
172
+ // SAFETY: We've validated that all bytes in `data` are valid UTF-8
173
+ // in the validation pass on line 30.
174
+ let s = unsafe { std::str::from_utf8_unchecked(data) };
175
+ ```
176
+
177
+ ### Minimizing Unsafe Surface
178
+
179
+ ```rust
180
+ // Wrap unsafe in safe abstractions with narrow interfaces
181
+ pub struct AlignedBuffer {
182
+ ptr: *mut u8,
183
+ len: usize,
184
+ cap: usize,
185
+ }
186
+
187
+ impl AlignedBuffer {
188
+ pub fn new(capacity: usize, alignment: usize) -> Self {
189
+ let layout = Layout::from_size_align(capacity, alignment).unwrap();
190
+ // SAFETY: layout is valid (non-zero size, power-of-two alignment)
191
+ let ptr = unsafe { alloc::alloc(layout) };
192
+ if ptr.is_null() {
193
+ alloc::handle_alloc_error(layout);
194
+ }
195
+ Self { ptr, len: 0, cap: capacity }
196
+ }
197
+
198
+ // Public API is fully safe — unsafe is encapsulated
199
+ pub fn push(&mut self, byte: u8) {
200
+ assert!(self.len < self.cap, "buffer full");
201
+ // SAFETY: We've verified len < cap, so ptr.add(len) is within allocation
202
+ unsafe { self.ptr.add(self.len).write(byte); }
203
+ self.len += 1;
204
+ }
205
+
206
+ pub fn as_slice(&self) -> &[u8] {
207
+ // SAFETY: ptr is valid for len bytes, all initialized by push()
208
+ unsafe { std::slice::from_raw_parts(self.ptr, self.len) }
209
+ }
210
+ }
211
+
212
+ impl Drop for AlignedBuffer {
213
+ fn drop(&mut self) {
214
+ let layout = Layout::from_size_align(self.cap, /* alignment */).unwrap();
215
+ // SAFETY: ptr was allocated with this layout in new()
216
+ unsafe { alloc::dealloc(self.ptr, layout); }
217
+ }
218
+ }
219
+ ```
220
+
221
+ ### Miri
222
+
223
+ ```bash
224
+ # Miri detects undefined behavior in unsafe code
225
+ cargo +nightly miri test
226
+
227
+ # Miri catches:
228
+ # - Out-of-bounds memory access
229
+ # - Use-after-free
230
+ # - Invalid use of uninitialized data
231
+ # - Violations of aliasing rules (Stacked Borrows)
232
+ # - Data races
233
+
234
+ # Run Miri in CI for any crate with unsafe code
235
+ ```
236
+
237
+ ## Anti-Patterns
238
+
239
+ ```rust
240
+ // Never: unsafe to "shut up the borrow checker"
241
+ // If the borrow checker rejects it, there's a reason. Redesign.
242
+
243
+ // Never: unsafe without a SAFETY comment
244
+ unsafe { ptr::write(dst, src) } // WHY is this safe? Document it.
245
+
246
+ // Never: transmute as a first resort
247
+ let x: u32 = unsafe { std::mem::transmute(my_float) };
248
+ // Use to_bits() / from_bits() instead — safe, clear, correct
249
+
250
+ // Never: Assuming layout without repr(C)
251
+ // Rust's default repr can reorder fields — don't assume memory layout
252
+
253
+ // Never: Dereferencing raw pointers without proving validity
254
+ // A raw pointer might be null, dangling, or misaligned.
255
+ // Prove all three are impossible before dereferencing.
256
+ ```
@@ -0,0 +1,300 @@
1
+ # Rust Testing
2
+
3
+ Rust has testing built into the language and toolchain. `cargo test` runs unit tests, integration tests, and doc tests in one command.
4
+
5
+ ## Unit Tests
6
+
7
+ ### Module-Level Tests
8
+
9
+ ```rust
10
+ // Tests live in the same file as the code they test
11
+ pub fn celsius_to_fahrenheit(c: f64) -> f64 {
12
+ c * 9.0 / 5.0 + 32.0
13
+ }
14
+
15
+ #[cfg(test)]
16
+ mod tests {
17
+ use super::*;
18
+
19
+ #[test]
20
+ fn freezing_point() {
21
+ assert!((celsius_to_fahrenheit(0.0) - 32.0).abs() < f64::EPSILON);
22
+ }
23
+
24
+ #[test]
25
+ fn boiling_point() {
26
+ assert!((celsius_to_fahrenheit(100.0) - 212.0).abs() < f64::EPSILON);
27
+ }
28
+
29
+ #[test]
30
+ fn negative_temperature() {
31
+ assert!((celsius_to_fahrenheit(-40.0) - -40.0).abs() < f64::EPSILON);
32
+ }
33
+ }
34
+ ```
35
+
36
+ ### Testing Error Cases
37
+
38
+ ```rust
39
+ #[test]
40
+ fn parse_invalid_input_returns_error() {
41
+ let result = parse_config("not valid toml");
42
+ assert!(result.is_err());
43
+
44
+ let err = result.unwrap_err();
45
+ assert!(err.to_string().contains("expected"));
46
+ }
47
+
48
+ #[test]
49
+ fn empty_name_fails_validation() {
50
+ let input = CreateUserInput { name: "".into(), email: "a@b.com".into() };
51
+ let result = validate_user(&input);
52
+
53
+ assert!(matches!(result, Err(ValidationError::EmptyField { field }) if field == "name"));
54
+ }
55
+
56
+ // Testing that something panics
57
+ #[test]
58
+ #[should_panic(expected = "index out of bounds")]
59
+ fn panics_on_out_of_bounds() {
60
+ let v = vec![1, 2, 3];
61
+ let _ = v[10];
62
+ }
63
+ ```
64
+
65
+ ### Test Organization
66
+
67
+ ```rust
68
+ // #[cfg(test)] ensures test code is never compiled into release builds
69
+ #[cfg(test)]
70
+ mod tests {
71
+ use super::*;
72
+
73
+ // Shared test fixtures
74
+ fn sample_user() -> User {
75
+ User {
76
+ id: UserId::new("test-123"),
77
+ name: "Alice".into(),
78
+ email: "alice@example.com".into(),
79
+ }
80
+ }
81
+
82
+ // Group related tests with nested modules
83
+ mod validation {
84
+ use super::*;
85
+
86
+ #[test]
87
+ fn rejects_empty_name() { ... }
88
+
89
+ #[test]
90
+ fn rejects_invalid_email() { ... }
91
+ }
92
+
93
+ mod serialization {
94
+ use super::*;
95
+
96
+ #[test]
97
+ fn round_trips_through_json() { ... }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Integration Tests
103
+
104
+ ```rust
105
+ // tests/api_test.rs — separate compilation unit, tests public API only
106
+ use my_crate::{Config, Server};
107
+
108
+ #[test]
109
+ fn server_responds_to_health_check() {
110
+ let config = Config::default();
111
+ let server = Server::new(config);
112
+
113
+ let response = server.handle_request("/healthz");
114
+
115
+ assert_eq!(response.status(), 200);
116
+ assert_eq!(response.body(), r#"{"status":"ok"}"#);
117
+ }
118
+ ```
119
+
120
+ ## Async Tests
121
+
122
+ ```rust
123
+ #[tokio::test]
124
+ async fn fetches_user_by_id() {
125
+ let db = setup_test_db().await;
126
+ let repo = UserRepo::new(db);
127
+
128
+ let user = repo.find_by_id("123").await.unwrap();
129
+
130
+ assert_eq!(user.name, "Alice");
131
+ }
132
+
133
+ // With custom runtime config
134
+ #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
135
+ async fn concurrent_operations() {
136
+ // ...
137
+ }
138
+ ```
139
+
140
+ ## Property-Based Testing
141
+
142
+ ```rust
143
+ use proptest::prelude::*;
144
+
145
+ proptest! {
146
+ #[test]
147
+ fn parse_never_panics(s in "\\PC*") {
148
+ // Whatever string we throw at parse, it must not panic
149
+ let _ = parse_input(&s);
150
+ }
151
+
152
+ #[test]
153
+ fn serialization_round_trips(
154
+ name in "[a-zA-Z]{1,50}",
155
+ age in 0u32..150,
156
+ ) {
157
+ let user = User { name, age };
158
+ let json = serde_json::to_string(&user).unwrap();
159
+ let decoded: User = serde_json::from_str(&json).unwrap();
160
+ assert_eq!(user, decoded);
161
+ }
162
+ }
163
+ ```
164
+
165
+ ## Doc Tests
166
+
167
+ ```rust
168
+ /// Adds two numbers together.
169
+ ///
170
+ /// # Examples
171
+ ///
172
+ /// ```
173
+ /// use my_crate::add;
174
+ ///
175
+ /// assert_eq!(add(2, 3), 5);
176
+ /// assert_eq!(add(-1, 1), 0);
177
+ /// ```
178
+ ///
179
+ /// # Panics
180
+ ///
181
+ /// Panics if the result overflows.
182
+ ///
183
+ /// ```should_panic
184
+ /// use my_crate::add;
185
+ ///
186
+ /// add(i64::MAX, 1); // Overflow!
187
+ /// ```
188
+ pub fn add(a: i64, b: i64) -> i64 {
189
+ a.checked_add(b).expect("overflow")
190
+ }
191
+
192
+ // Doc tests are compiled and run — they're both documentation AND tests.
193
+ // They verify your examples actually work.
194
+ ```
195
+
196
+ ## Mocking and Test Doubles
197
+
198
+ ```rust
199
+ // Prefer trait-based dependency injection over mocking frameworks
200
+
201
+ // Define trait at the consumer
202
+ trait Clock {
203
+ fn now(&self) -> DateTime<Utc>;
204
+ }
205
+
206
+ // Production implementation
207
+ struct SystemClock;
208
+ impl Clock for SystemClock {
209
+ fn now(&self) -> DateTime<Utc> { Utc::now() }
210
+ }
211
+
212
+ // Test implementation — deterministic
213
+ struct FakeClock(DateTime<Utc>);
214
+ impl Clock for FakeClock {
215
+ fn now(&self) -> DateTime<Utc> { self.0 }
216
+ }
217
+
218
+ #[test]
219
+ fn token_expires_after_one_hour() {
220
+ let fixed_time = Utc.with_ymd_and_hms(2025, 1, 1, 12, 0, 0).unwrap();
221
+ let clock = FakeClock(fixed_time);
222
+ let token = generate_token(&clock);
223
+
224
+ let check_time = fixed_time + Duration::hours(2);
225
+ assert!(token.is_expired_at(check_time));
226
+ }
227
+
228
+ // When you do need a mocking library: mockall
229
+ #[automock]
230
+ trait Database {
231
+ fn get(&self, key: &str) -> Option<String>;
232
+ }
233
+
234
+ #[test]
235
+ fn returns_cached_value() {
236
+ let mut mock = MockDatabase::new();
237
+ mock.expect_get()
238
+ .with(eq("user:123"))
239
+ .returning(|_| Some("Alice".into()));
240
+
241
+ let service = Service::new(mock);
242
+ assert_eq!(service.get_user("123"), Some("Alice".into()));
243
+ }
244
+ ```
245
+
246
+ ## Benchmarks
247
+
248
+ ```rust
249
+ // Using criterion (de facto standard)
250
+ // benches/my_benchmark.rs
251
+ use criterion::{criterion_group, criterion_main, Criterion, black_box};
252
+
253
+ fn bench_parse(c: &mut Criterion) {
254
+ let input = include_str!("../testdata/large_input.txt");
255
+
256
+ c.bench_function("parse_large_input", |b| {
257
+ b.iter(|| parse(black_box(input)))
258
+ });
259
+ }
260
+
261
+ fn bench_comparison(c: &mut Criterion) {
262
+ let mut group = c.benchmark_group("serialization");
263
+ let data = generate_test_data();
264
+
265
+ group.bench_function("json", |b| {
266
+ b.iter(|| serde_json::to_vec(black_box(&data)))
267
+ });
268
+
269
+ group.bench_function("bincode", |b| {
270
+ b.iter(|| bincode::serialize(black_box(&data)))
271
+ });
272
+
273
+ group.finish();
274
+ }
275
+
276
+ criterion_group!(benches, bench_parse, bench_comparison);
277
+ criterion_main!(benches);
278
+ ```
279
+
280
+ ## Test Anti-Patterns
281
+
282
+ ```rust
283
+ // Never: Tests that depend on execution order
284
+ // Each test runs in its own thread — no shared mutable state
285
+
286
+ // Never: Ignoring tests instead of fixing them
287
+ #[ignore] // Why? For how long? File an issue.
288
+
289
+ // Never: Testing private internals through hacks
290
+ // If you can't test it through the public API, the design needs work
291
+
292
+ // Never: Assertions without messages in complex tests
293
+ assert!(result.is_ok()); // What was the input? What was the error?
294
+ // Better:
295
+ assert!(result.is_ok(), "expected Ok for input {input:?}, got {result:?}");
296
+
297
+ // Never: Flaky tests
298
+ // Flaky = broken. Fix the race condition or the timing dependency.
299
+ // Use deterministic clocks, seeded RNGs, and proper synchronization.
300
+ ```