{
// ... validation ...
Ok(to_decimal_i64(csd)) // โ full i64 precision!
}
```
The **macro prevented this bug** because each generated function calls the correct type-specific inner function via `$inner_fn:ident`.
> ๐ฏ **Macros don't just reduce duplication โ they eliminate whole classes of bugs.**
---
### Pitfall 3: Trapped in `mod tests` ๐ช
Before the refactoring, `to_csd_i64` and `to_csd_i128` lived in `mod tests`:
```rust
mod tests {
// These looked public, but were only visible within tests
pub fn to_csd_i64(decimal_value: i64) -> String { ... }
pub fn to_csd_i128(decimal_value: i128) -> String { ... }
}
```
**The trap:**
- โ
Unit tests could use them โ **false confidence**
- โ External code could **never** call them
- โ `cargo doc` didn't show them
- โ Doc-tests failed (compiled as external crate)
> **If a function should be public, make it public at the module level โ not inside `mod tests`.**
---
class: nord-light, middle, center
## ๐งช Testing & Lessons
---
### ๐งช All Tests Pass: 170+ โ
.mermaid[
graph TD
CSD_Unit["CSD Unit\n88 โ
"] --> All["All Pass\n๐"]
CSD_Main["CSD CLI\n22 โ
"] --> All
CSD_Integ["CSD Integration\n25 โ
"] --> All
CSD_Prop["CSD Property\n4 โ
"] --> All
CSD_Doc["CSD Doc-Tests\n21 โ
"] --> All
RT_Unit["rat-trig Unit\n149 โ
"] --> All
RT_Integ["rat-trig Integ\n10 โ
"] --> All
RT_Doc["rat-trig Doc\n14 โ
"] --> All
Cpp["C++ Tests\n74 โ
"] --> All
style CSD_Unit fill:#4caf50,color:#fff
style CSD_Main fill:#66bb6a,color:#fff
style CSD_Integ fill:#81c784,color:#fff
style CSD_Prop fill:#a5d6a7,color:#fff
style CSD_Doc fill:#c8e6c9,color:#fff
style RT_Unit fill:#90caf9,color:#fff
style RT_Integ fill:#bbdefb,color:#fff
style RT_Doc fill:#e3f2fd,color:#fff
style Cpp fill:#ffcc80,color:#fff
style All fill:#ff9800,stroke:#e65100,stroke-width:3px
]
```
CSD: 139 tests + 21 doc-tests = 160 โ
rat-trig: 149 tests + 10 integ + 14 doc-tests = 173 โ
C++: 74 tests + 233 assertions โ
```
---
### Key Takeaways ๐ฏ
.pull-left[
.mermaid[
graph TD
M["Declarative Macros\nmacro_rules!"] --> D["DRY Code\nOne algorithm, N types"]
M --> C["Compile-time\nZero runtime cost"]
M --> E["Exported\nAll variants public"]
D --> T["170+ tests\nall pass โ
"]
C --> I["Inline-expanded\nby compiler ๐ฆ"]
E --> B["Bug found:\ntruncation in\n_result variants ๐"]
style M fill:#ff9800,stroke:#e65100,stroke-width:3px
style D fill:#e3f2fd
style C fill:#e3f2fd
style E fill:#e3f2fd
style T fill:#4caf50,color:#fff
style I fill:#2196f3,color:#fff
style B fill:#f44336,color:#fff
]
]
.pull-right[
1. **Macros eliminate duplication** โ 9 macros replaced 34 hand-written functions across 2 libraries
2. **`pub const fn` inside macros: works for pure arithmetic, fragile for `panic!()` + string ops** โ always verify with `cargo test --doc` and `nm`
3. **Test-module code creates false confidence** โ if it should be public, make it so
4. **`as` casts truncate silently** โ macros prevent this by dispatching to correct types
5. **Zero overhead** โ macros expand at compile time, identical to hand-written code
]
---
### Macro Best Practices ๐
.pull-left[
**โ
Do:**
- Use macros when the **same algorithm** applies to multiple types
- Pass helper function names as `$fn:ident` parameters for composability
- Split signed/unsigned macros when subtraction semantics differ
- Verify `pub const fn` in macros with `cargo test --doc` + `nm` on .rlib
]
.pull-right[
**โ Don't:**
- Use `pub const fn` in macros with `panic!()`, string ops, or heap types โ use `pub fn`
- Hide generated functions in `mod tests`
- Use `as` casts between width-varying types
- Assume macro-generated items behave identically to hand-written ones
]
**๐ Verify with:**
```bash
cargo test --doc # doc-tests = external crate view
nm target/debug/*.rlib # check symbols in compiled output
cargo doc --no-deps # verify documentation generation
```
---
### Why Wasn't This Done Before? ๐คท
The macro refactoring took ~20 minutes per library. So why did the duplication exist for so long?
.mermaid[
graph TD
Start["Original Rust Code\nhad duplication"] --> R1["๐งฑ Cargo-cult:\n'C++ uses templates,\nport 1:1 to fn'"]
Start --> R2["๐ Nobody reviewed\n`mod tests`:\n'they compile, they pass'"]
Start --> R3["๐ Incremental dev\nwithout cleanup:\n'add i64 quick, fix later'"]
Start --> R4["๐ค AI didn't flag it:\n'following existing\npattern blindly'"]
R1 --> Result["โ 10 + 24 hand-written fns\ninstead of 3 + 6 macros"]
R2 --> Result
R3 --> Result
R4 --> Result
style Start fill:#ff9800,stroke:#e65100
style R1 fill:#e3f2fd
style R2 fill:#e3f2fd
style R3 fill:#e3f2fd
style R4 fill:#e3f2fd
style Result fill:#f44336,color:#fff
]
---
**The four forces that created the duplication:**
1. **Cargo-cult from C++** โ templates โ concrete fns, missing Rust's `macro_rules!`
2. **Test-module blind spot** โ `pub fn` inside `mod tests` looks public, isn't
3. **Incremental rot** โ "just add i64 real quick" โ permanent fixture
4. **AI pattern-matching** โ followed existing style instead of questioning it
> ๐ก **Lesson:** The same forces that create tech debt also make AI amplify it. Always ask: *"Is there an idiomatic way to do this in **this** language?"* not *"How was it done before?"*
---
count: false
class: nord-dark, middle, center
# ๐ Q&A
**Rust Macros: Zero-Cost Abstraction for Type Specialization**
*What questions do you have?* ๐ค
---
count: false
class: nord-dark, middle, center
# ๐ Thank You!
## Write Code Once, Use Everywhere with Macros! ๐ฆ ๐งฉ ๐