# batch-impl
为 Rust trait 批量生成 `impl` 块的过程宏库——**一行 DSL,展开成 N 个 impl**。
```rust
use batch_impl::batch_impl;
# use std::rc::Rc;
// 一个 body,为 4 种类型各生成一个 impl
#[batch_impl(<T> Sortable<T> [Box, Rc]^Vec<T> where{ T: Ord } {
fn is_sorted(&self) -> bool { self.windows(2).all(|w| w[0] <= w[1]) }
})]
trait Sortable<T> { fn is_sorted(&self) -> bool; }
// → impl<T> Sortable<T> for Box<Vec<T>> where T: Ord { ... }
// → impl<T> Sortable<T> for Rc<Vec<T>> where T: Ord { ... }
// 一行生成 4 个带泛型的元组 impl
#[batch_impl(()^4)]
trait TupleTrait {}
// → impl<A> TupleTrait for (A,) {}
// → impl<A, B> TupleTrait for (A, B) {}
// → impl<A, B, C> TupleTrait for (A, B, C) {}
// → impl<A, B, C, D> TupleTrait for (A, B, C, D) {}
```
## 核心心智模型
你写的是**一条"类型矩阵"的描述**,batch-impl 对矩阵的每个格子生成 impl:
```
#[batch_impl( <impl-泛型> Trait名<trait-泛型> 目标类型矩阵 { body }? )]
```
| `^` / `-` | 应用:把左侧容器/修饰符作用到右侧类型 | **同一个运算**,仅结合性不同 |
| `[A, B]` | 列表 | 横向展开(笛卡尔积) |
| `(A, B)` | 元组 | 排列(有序对) |
| `#name` | 指令:从 trait 定义自动抄 item 签名 | body 不用手写签名 |
`^` 与 `-` 是**同一运算**(左侧是修饰符/容器,右侧是目标类型),区别只在结合方向:
- `^` **右结合**,链式产生嵌套:`Box^Box^T` = `Box<Box<T>>`,`HashMap^K^V` = `HashMap<K<V>>`
- `-` **左结合**,链式累加参数:`HashMap-K-V` = `HashMap<K, V>`,`fn(A, B)-C` = `fn(A, B) -> C`
所以选哪个只看你想要的分组形状:想套娃用 `^`,想并列参数用 `-`。
`[A, B]^[X, Y]` = 2×2 矩阵(4 个 impl);`(T1, T2)^2` = 排列(4 个有序对)。
## 快速开始
```toml
[dependencies]
batch-impl = "0.5.1"
```
需要 Rust 2024 edition 及以上。
```rust
use batch_impl::batch_impl;
// 1. 定义 trait,方法签名只写一次
trait Describe { fn describe(&self) -> String; }
// 2. 写一条 DSL:目标类型 + body(方法签名用 #fill 自动从 trait 抄)
#[batch_impl(
[usize, isize] #fill(name){"number"},
String #fill(name){"string"}
)]
trait Tagged { fn name(&self) -> &str; }
// → impl Tagged for usize { fn name(&self) -> &str { "number" } }
// → impl Tagged for isize { fn name(&self) -> &str { "number" } }
// → impl Tagged for String { fn name(&self) -> &str { "string" } }
```
## 语法参考
### spec 结构
| `<impl-泛型>` | `<T>`, `<T: Clone>`, `<const N: usize>` | impl 块需要泛型参数时 |
| `Trait名<trait-泛型>` | `MyTrait<T>`, `MyTrait<Vec<T>>` | trait 定义有泛型参数时 |
| 目标类型 | `usize`, `Vec<T>`, `&str` | 必需 |
| `[...]` 列表 | `[A, B, C]` | 为多个类型同时实现 |
| `{ body }` | `{ fn m(&self) -> usize { 0 } }` | 需要自定义实现体时 |
多个 spec 用 `,` 分隔:`#[batch_impl(usize, isize)]`。
### 运算符
DSL 通过四级优先级解析(从低到高):
| 0 | `;` | — | `batch_trait!` 的段落分隔符 |
| 1 | `,` | — | impl-spec 列表分隔 |
| 2 | `-` | 左结合 | 应用(同 `^` 语义,链式累加参数) |
| 3 | `^` | 右结合 | 应用(链式嵌套) |
`(` `)` 分组在所有运算符之上起作用。
结合示例:
- `A^B-C,D` = `(A^B)-C,D` = `A<B,C>,D`
- `[A,B]^[C,D]-E` = `([A,B]^[C,D])-E` = `[A<C>,A<D>,B<C>,B<D>]-E`
- `HashMap^K-V` = `(HashMap^K)-V` = `HashMap<K>-V` = `HashMap<K, V>`
- `fn^(A,B)-C` = `(fn^(A,B))-C` = `fn(A,B)->C`
> **注意**:`Box^Vec-u32` 是错误写法(会被解释为 `Box<Vec, u32>`),应写为 `Box^Vec^u32`。
### `^` 修饰符
左侧的修饰符可以是:
| `&` | 引用 |
| `&mut` | 可变引用 |
| `*const` | 裸指针(不可变) |
| `*mut` | 裸指针(可变) |
| `self` | 恒等(不改变类型) |
| `unsafe` | 标记 impl 为 `unsafe impl` |
| `fn` | 函数类型前缀 |
| `#[attr]` | 属性前缀 |
| `Ident` | 容器(如 `Box`, `Vec`) |
| `Ident<...>` | 带预填泛型的容器(如 `HashMap<K>`),`^` 追加参数 |
| `(A,)`/`(A,B)` | 元组前缀 |
| `()` | 空元组前缀 |
| `(<bound>)` | 带 trait bound 的泛型元组前缀 |
| `[A, B]` | 多修饰符(笛卡尔积展开) |
| `&^T` | `&T` |
| `&mut^T` | `&mut T` |
| `*const^T` | `*const T` |
| `*mut^T` | `*mut T` |
| `self^T` | `T` |
| `Box^T` | `Box<T>` |
| `Box^<X,Y>` | `Box<X, Y>`(多参容器) |
| `[Box, Vec]^T` | `Box<T>, Vec<T>` |
| `Box^[T1, T2]` | `Box<T1>, Box<T2>` |
| `[Box, Vec]^[T1, T2]` | 笛卡尔积共 4 项 |
| `Box^Box^T` | `Box<Box<T>>`(右结合嵌套) |
| `HashMap<K>^V` | `HashMap<K, V>`(预填泛型追加) |
| `[HashMap<K>, Vec<K>]^V` | `HashMap<K, V>, Vec<K, V>` |
| `&^Box^T` | `&Box<T>`(修饰符链式应用) |
| `*const^Vec^T` | `*const Vec<T>` |
| `fn^(A,B)` | `fn(A,B)`(函数类型) |
| `#[attr]^T` | 在 impl 块前添加属性 |
### `-` 运算符
与 `^` 同一运算,仅左结合(链式累加参数):
| `Vec-u32` | `Vec<u32>` |
| `HashMap-u32-String` | `HashMap<u32, String>`(左结合,参数累加) |
| `()-[A, B]` | `(A,), (B,)` |
| `()-[A, B]-[C, D]` | `(A, C), (A, D), (B, C), (B, D)` |
### 元组生成
`^` 右侧是数字或范围时,生成指定长度的元组(数字只作为指数使用,u8 范围):
| `()^3` | `(A, B, C)`(带3个泛型参数) |
| `(T,)^3` | `(T, T, T)` |
| `(<Clone>)^3` | `(A:Clone, B:Clone, C:Clone)` |
| `(T1, T2)^2` | 笛卡尔积 `(T1,T1), (T1,T2), (T2,T1), (T2,T2)` |
| `()^1..3` | `(A,), (A, B)`(长度1到2) |
| `()^1..=3` | `(A,), (A, B), (A, B, C)`(长度1到3) |
| `(T,)^2..4` | `(T, T), (T, T, T)`(长度2到3) |
> 注意:`(T)` 是分组(非元组),`(T,)` 才是单元素元组。
### 歧义处理
- **`[]`**:有逗号是并列列表,无逗号是切片类型(如 `Box^[u32]` → `Box<[u32]>`)
- **`()`**:`()` = 空元组,`(A,)` = 单元素元组,`(A)` = 分组
- **`()^0`**:生成空元组 `()`,即 `impl Trait for ()`
- **`[T; N]`**:`[]` 内的 `;` 通过 DSL 的 `Semi` 优先级层级识别为定长数组分隔符
## 组合拳
### 共享 body + 列表
一个 body 为所有目标类型复用:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl([usize, isize, f32] {
fn tag(&self) -> &'static str { "number" }
})]
trait Tagged { fn tag(&self) -> &'static str; }
```
### 嵌套泛型合并
列表项各自声明 impl 泛型,自动合并到 impl 块:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
use std::collections::HashMap;
#[batch_impl(<T> Describe<T> [Vec<T>, <U> HashMap<T, U>] {
fn describe(&self) -> String { format!("len={}", self.len()) }
})]
trait Describe<T> { fn describe(&self) -> String; }
// → impl<T> Describe<T> for Vec<T>
// → impl<T, U> Describe<T> for HashMap<T, U>
```
### 独立/共享 body 合并
列表项可有独立 body,与共享 body 合并:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(
[usize { fn name() -> &'static str { "usize" } },
isize { fn name() -> &'static str { "isize" } }]
{ fn zero() -> Self { 0 } }
)]
trait Zero {
fn zero() -> Self;
fn name() -> &'static str;
}
// → impl Zero for usize { fn zero() -> Self { 0 } fn name() -> &'static str { "usize" } }
// → impl Zero for isize { fn zero() -> Self { 0 } fn name() -> &'static str { "isize" } }
```
### 关联类型简洁写法
`Name=value` 语法在 trait 泛型参数中绑定关联类型:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(<T> Iter<Item=T> Vec<T> {
fn count(&self) -> usize { self.len() }
})]
trait Iter {
type Item;
fn count(&self) -> usize;
}
// → impl<T> Iter for Vec<T> { type Item = T; fn count(&self) -> usize { self.len() } }
```
支持多关联类型与泛型约束:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(<T, U> Pair<First=T, Second=U> (T, U))]
trait Pair {
type First;
type Second;
}
#[batch_impl(<T: Clone> CloneIter<Item=T> Vec<T> {
fn first(&self) -> T { self[0].clone() }
})]
trait CloneIter {
type Item;
fn first(&self) -> Self::Item;
}
```
### 指令系统
`#` 指令在预处理阶段展开,从 trait 定义自动读取 item 签名/类型,body 不用手写签名。
**`#name{body}` — 单 item 赋值**(fn / const / type 自动选择输出格式):
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(usize #to_str{"usize"})]
trait ToString { fn to_str(&self) -> &str; }
// → impl ToString for usize { fn to_str(&self) -> &str { "usize" } }
#[batch_impl(usize #MAX_SIZE{1024})]
trait HasConst { const MAX_SIZE: usize; }
// → impl HasConst for usize { const MAX_SIZE: usize = 1024; }
#[batch_impl(usize #Item{u32})]
trait HasType { type Item; }
// → impl HasType for usize { type Item = u32; }
```
**`#fill(methods){body}` — 多方法同一 body**:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(usize #fill(name, kind){"usize"})]
trait Describable { fn name(&self) -> &str; fn kind(&self) -> &str; }
// → 为 name 和 kind 各生成 { "usize" } body
```
特殊标记:`#all`(所有 item)、`#all_methods`(仅 fn)、`#all_constants`(仅 const)、`#all_types`(仅 type)。
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(usize #fill(#all){"default"})]
trait HasAll { fn method(&self) -> &str; const VALUE: &str; }
// → fn method 与 const VALUE 各生成 { "default" } body
```
**`#delegate(methods){target}` — 委托调用**:把方法委托到 target 表达式上调用同名方法。
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
// Vec<u32> 用 #name 提供 body,Box<Vec<u32>> 委托过去
#[batch_impl(
Vec<u32> #d_len{self.len()},
Box^Vec^u32 #delegate(d_len){**self}
)]
trait MyLen { fn d_len(&self) -> usize; }
// → impl MyLen for Box<Vec<u32>> { fn d_len(&self) -> usize { (**self).d_len() } }
// blanket impl 模式:具体类型 + 引用委托
#[batch_impl(i32 #to_i32{*self}, <T: ToI32> &T #delegate(to_i32){**self})]
trait ToI32 { fn to_i32(&self) -> i32; }
// → impl<T: ToI32> ToI32 for &T { fn to_i32(&self) -> i32 { (**self).to_i32() } }
```
### 指令与 DSL 组合
指令可与运算符、`{body}` 连续附着自由组合:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(
usize #name{"usize"} { fn kind(&self) -> &str { "number" } }
)]
trait Tagged { fn name(&self) -> &str; fn kind(&self) -> &str; }
#[batch_impl(<T: std::fmt::Display> Vec<T> #t10{self.len()})]
trait Len { fn t10(&self) -> usize; }
```
**扩展机制**:不认识的 `#name` 自动转换为 `#[name[(args){body}]]` 属性,交给用户的属性宏处理。工作流程:预处理器遇到 `#my_handler(args){body}` → 不认识 `my_handler` → 生成 `#[my_handler[(args){body}]]` → DSL 解析器当普通属性节点处理 → 编译器随后调用用户的 `#[my_handler]` 属性宏。这意味着指令系统是**开放的**。
### `where{...}` — where 子句
`where{...}` 后缀跟在目标类型之后,内是透传的 where 谓词;多个会合并:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(<T: Clone> Sortable<T> Vec<T> where{ T: Ord } {
fn sort(&self) -> Vec<T> { let mut v = self.clone(); v.sort(); v }
})]
trait Sortable<T> { fn sort(&self) -> Vec<T>; }
// → impl<T: Clone> Sortable<T> for Vec<T> where T: Ord { ... }
#[batch_impl(<A> <B> PairAB<A, B> (A, B) where{A: Clone} where{B: Clone} {
fn pair(&self) -> (A, B) { (self.0.clone(), self.1.clone()) }
})]
trait PairAB<A, B> { fn pair(&self) -> (A, B); }
```
也支持 Rust 风格裸写 `where 谓词 {代码块}`(三个接口通用),谓词后的 `{...}` 代码块必须存在;谓词区边界为首个 `{...}` 代码块(`ident!{...}` 宏调用体与 `<N = {5}>` 尖括号内代码块不计入),逗号谓词不会被 spec 切分:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(<A> <B> PairAB<A, B> (A, B) where A: Clone, B: Clone {
fn pair(&self) -> (A, B) { (self.0.clone(), self.1.clone()) }
})]
trait PairAB<A, B> { fn pair(&self) -> (A, B); }
// → impl<A, B> PairAB<A, B> for (A, B) where A: Clone, B: Clone { ... }
```
多个 `where` 段可依次书写(`where A: Clone where B: Clone`),与旧式多 `where{...}` 等价。
### fn 类型
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(fn^(i32, u32))]
trait FnSimple {}
// fn 类型追加返回类型
#[batch_impl(fn(i32, u32)-String)]
trait FnWithReturn {}
// fn 类型批量生成(笛卡尔积)
#[batch_impl(fn-(i32, u32)^2)]
trait FnTupleGen {}
// → impl FnTupleGen for fn(i32, i32) {}
// → impl FnTupleGen for fn(i32, u32) {}
// → impl FnTupleGen for fn(u32, i32) {}
// → impl FnTupleGen for fn(u32, u32) {}
```
### unsafe / 指针 / 属性
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(unsafe^usize, isize)]
unsafe trait UnsafePartial {}
// unsafe trait 的所有 impl 自动 unsafe
#[batch_impl(*const^u32, *mut^i32)]
trait PtrMarker {}
#[batch_impl(*const^Box^u32)]
trait ConstPtrChain {}
// → impl ConstPtrChain for *const Box<u32> {}
#[batch_impl(#[allow(dead_code)]^usize, isize)]
trait AttrSimple {}
```
### 复杂类型透传
无法识别的类型原样透传:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
#[batch_impl(
(i32, String),
&str,
Box<dyn std::fmt::Display>,
fn(i32) -> bool,
dyn Fn() + Send + Sync
)]
trait ComplexMarker {}
```
## 三个入口
| `#[batch_impl]` | 属性宏,在 trait 定义上标注,宏参数即 DSL |
| `#[batch_impl_only]` | 同上,但丢弃 trait 定义,只输出 impl 块 |
| `batch_trait!` | 函数式宏,对已声明的 trait 批量生成 impl(支持多 trait) |
三者接受相同的 DSL 参数。
### `#[batch_impl_only]`
trait 已在别处定义、只需批量生成 impl 的场景。trait 定义仍要写出(只用来读取方法签名),输出不含 trait:
```rust
# use batch_impl::{batch_impl, batch_impl_only, batch_trait};
# trait Greet { fn hello(&self) -> &str; } // 真实 trait 在别处定义
#[batch_impl_only(usize #hello{"hi"})]
trait Greet { fn hello(&self) -> &str; } // 此 dummy 定义被丢弃
// → impl Greet for usize { fn hello(&self) -> &str { "hi" } }
```
支持 `#path::to::Trait:` 路径前缀,为外部模块中定义的 trait 生成 impl(路径末尾标识符必须与本地 dummy trait 名一致;`#[batch_impl]` 不支持此前缀):
```ignore
// 路径前缀需要真实的外部模块上下文:doctest 的 fn main 内无法定义 pub 模块,
// 故此处仅示意语法(`mod` 是关键字不能作路径段,实际应为合法模块名)。
// 该特性的编译行为由 tests/regression.rs 中的路径 trait 用例覆盖。
#[batch_impl_only(#ext::traits::TraitName: usize, isize)]
trait TraitName { }
```
### `batch_trait!`
对已声明的 trait 批量生成 impl,`;` 分隔多个 trait 段。语法:`[unsafe] Trait路径: impl-specs`,接受与 `#[batch_impl]` 完全相同的 DSL 语法(`:` 右侧),额外支持多 trait 段、路径 trait(如 `foo::C`,见 tests/regression.rs)、unsafe 段:
```rust
use batch_impl::batch_trait;
trait A {}
trait B<T> {}
unsafe trait UnsafeTrait {}
batch_trait!(
A: usize, isize;
B: <T> B<T> Vec<T>;
unsafe UnsafeTrait: usize
);
```
## 错误提示
所有 DSL 语法错误通过 `compile_error!()` 输出中文提示并指向源码位置,永不 panic:
| `batch_trait!(;)` | `batch_trait! 中期望 trait 名称` |
| `batch_trait!(A)` | `batch_trait! 中期望 ':' 分隔 trait 名称和 impl-specs` |
| `batch_trait!(A: B::)` | `batch_trait! 中期望标识符作为 trait 名称` |
| 裸 `where` 缺代码块 | `batch-impl: \`where\` 谓词后缺少代码块 {...}` |
## 内部架构
```text
lib.rs 宏入口 + 共享驱动(#[batch_impl] / #[batch_impl_only] / batch_trait!)
├── preprocess.rs 指令预处理:#name 指令展开(内置 + 自定义属性委托)
├── where_process.rs 裸 where 改写:`where 谓词 {body}` → 旧式 `where{谓词}`(预处理后、解析前,三接口共用)
├── preprocess_helpers.rs 预处理辅助:build_from_item / get_trait_item / collect_call_args
├── parse.rs DSL 解析器:Cursor 游标 + 优先级攀爬(Op::Semi/Comma/Dash/Caret/Prim)
├── parse_atom.rs 原子层解析:属性 / fn / 分组 / 前缀 / 范围
├── generic.rs 泛型与尖括号解析:parse_generic / parse_angle_bracket_contents
├── types.rs AST 节点(Ty 枚举 19 个变体,含 Error)+ Op 优先级定义;前缀/后缀包装内层用 Option<Box<Ty>> 表示裸状态
├── types_render.rs AST 渲染:ToTokens impl for Ty + params_to_tokens 系列
├── apply.rs 运算符语义:Apply trait + 核心 apply() 折叠规则(^ 右结合 / 数组分发)
├── apply_tuple.rs 元组与容器运算符:TyTuple / TyGroup / TyFn / TyWithPrefix 等的 Apply impl + 元组展开(^N / 笛卡尔积 / 范围)
├── scan.rs 扫描与游标:Cursor<'a> + scan_with + ScanMode::Lossy / Strict
├── batch_trait_entry.rs 共享驱动:BFS 展开并列列表 → 逐叶子 generate_impl
├── path_prefix.rs 外部 trait 路径前缀:#Path::to::Trait: 状态机解析
├── codegen.rs 代码生成:extract_impl_parts 递归拆解 → generate_impl 渲染 impl 块
└── diagnostic.rs 统一 compile_error_str(msg) 用于编译期诊断
```
解析流程:**token 流 → 指令预处理 → where 裸写改写 → Cursor 扫描取切片 → parse_item 优先级攀爬 → Ty AST → BFS 展开并列列表 → 逐叶子 generate_impl**
### 错误处理
所有 DSL 语法错误均通过 `compile_error!()` 输出友好的编译错误,**永不 panic**。`Ty::Error` 变体在 apply/codegen 链路中透传,`preprocess` 层通过 `Result<_, TokenStream>` 传播,并由 `diagnostic.rs::compile_error_str` 统一构造 `compile_error!` token 流。
### 测试
测试矩阵分三层:
| `examples/` | `quickstart.rs` | 可运行的 DSL 主特性 demo(`cargo run --example quickstart`),14 段覆盖基础→复杂场景 |
| `tests/` | `dsl.rs` | 27 个 `#[test]`,覆盖核心特性的语义回归(含 where 子句、外部路径前缀) |
| `tests/` | `regression.rs` | 16 个 `#[test]`,覆盖 dsl.rs 未触碰的 corner case:嵌套 `>>`、路径类型、const 泛型、生命周期、dyn + Send、`batch_impl` vs `batch_trait!` 一致性 |
| `tests/` | `ui.rs` | `trybuild` UI 测试:9 个 `compile_fail` fixture 锁定诊断措辞 + 1 个 `pass` fixture |
运行:
```bash
cargo run --example quickstart # 主特性 demo
cargo test --test dsl --test regression # 功能与回归测试
cargo test --test ui # 诊断 UI 测试
# 重新生成 UI 快照:
TRYBUILD=overwrite cargo test --test ui
```
## 许可证
MIT OR Apache-2.0