Skip to main content

Crate batch_impl

Crate batch_impl 

Source
Expand description

§batch-impl

为 Rust trait 批量生成 impl 块的过程宏库——一行 DSL,展开成 N 个 impl

use batch_impl::batch_impl;

// 一个 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 个有序对)。

§快速开始

[dependencies]
batch-impl = "0.5.1"

需要 Rust 2024 edition 及以上。

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^TT
Box^TBox<T>
Box^<X,Y>Box<X, Y>(多参容器)
[Box, Vec]^TBox<T>, Vec<T>
Box^[T1, T2]Box<T1>, Box<T2>
[Box, Vec]^[T1, T2]笛卡尔积共 4 项
Box^Box^TBox<Box<T>>(右结合嵌套)
HashMap<K>^VHashMap<K, V>(预填泛型追加)
[HashMap<K>, Vec<K>]^VHashMap<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-u32Vec<u32>
HashMap-u32-StringHashMap<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 为所有目标类型复用:

#[batch_impl([usize, isize, f32] {
    fn tag(&self) -> &'static str { "number" }
})]
trait Tagged { fn tag(&self) -> &'static str; }

§嵌套泛型合并

列表项各自声明 impl 泛型,自动合并到 impl 块:

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 合并:

#[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 泛型参数中绑定关联类型:

#[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() } }

支持多关联类型与泛型约束:

#[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 自动选择输出格式):

#[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

#[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)。

#[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 表达式上调用同名方法。

// 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} 连续附着自由组合:

#[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 谓词;多个会合并:

#[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 切分:

#[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 类型

#[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 / 指针 / 属性

#[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 {}

§复杂类型透传

无法识别的类型原样透传:

#[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:

#[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] 不支持此前缀):

// 路径前缀需要真实的外部模块上下文: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 段:

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` 谓词后缺少代码块 {…}`

§内部架构

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!() 输出友好的编译错误,永不 panicTy::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.rs27 个 #[test],覆盖核心特性的语义回归(含 where 子句、外部路径前缀)
tests/regression.rs16 个 #[test],覆盖 dsl.rs 未触碰的 corner case:嵌套 >>、路径类型、const 泛型、生命周期、dyn + Send、batch_impl vs batch_trait! 一致性
tests/ui.rstrybuild UI 测试:9 个 compile_fail fixture 锁定诊断措辞 + 1 个 pass fixture

运行:

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

Macros§

batch_trait
对已声明的 trait 批量生成 impl 块的函数式宏。

Attribute Macros§

batch_impl
为 trait 批量生成 impl 块的属性宏。
batch_impl_only
#[batch_impl] 相同,但丢弃 trait 定义本身,只输出 impl 块。