如何编写实现通用特征 fx 的过程宏。添加?

How to write a procedural macro that implements a generic trait, fx. Add?

我有一个 Module 特征,我想编写一个程序宏来实现 AddSubMul 以实现 Module 特征。

给定一个实现 Module 的结构 Foo<S>,生成的代码应该如下所示

impl <S, RHS: Module>  std::ops::Add<RHS> for Foo<S> {
    type Output = crate::modules::Add<Foo<S>, RHS>;

    fn add(self, rhs: RHS) -> Self::Output {
        crate::modules::Add::new(self, rhs)
    }
}

问题在于 struct 和 Add trait 都有自己的泛型类型。 如果不是这种情况,宏代码可能看起来像这样

let name = &ast.ident;
let generics = &ast.generics;
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
    impl #impl_generics std::ops::Add<RHS> for #name #ty_generics #where_clause {
        type Output = crate::modules::Add<#name, RHS>;
        
        fn add(self, rhs: RHS) -> Self::Output {
            crate::modules::Add::new(self, rhs)
        }
    }
}

但是 RHS 类型从未定义过。 我尝试了 <#impl_generics, RHS: Module> 等变体,但其中 none 有效。

显然,为 T: Module 全面实施 Add 会更好,但这是不可能的,因为不能为类型参数实施外部特征。参见 here。解决此问题的方法也可以解决我在这种特定情况下的问题,但似乎我所问的应该是可能的。

有什么办法吗?

我不确定为什么你的宏代码不起作用,但更简单的方法是为任何实现 ModuleT 编写一个全面的实现,并完全避免使用宏:

impl<T, Rhs> std::ops::Add<Rhs> for T
where
    T: Module,
    Rhs: Module,
{
    type Output = crate::modules::Add<T, Rhs>;

    fn add(self, rhs: Rhs) -> Self::Output {
        crate::modules::Add::new(self, rhs)
    }
}

解决方案是手动处理 Foo 的类型参数,而不是使用 generics.split_for_impl()。 我上面的例子的工作版本变成了。

let name = &ast.ident;
let generics = &ast.generics;
let type_params = generics.type_params();
let (_, ty_generics, where_clause) = generics.split_for_impl();
quote! {
    #ast // This is not a derive macro, sp this is necessary to keep the original struct definition.
    impl <#(#type_params),*, RHS: crate::modules::Module>  std::ops::Add<RHS> for #name #ty_generics #where_clause {
        type Output = crate::modules::Add<#name #ty_generics, RHS>;

        fn add(self, rhs: RHS) -> Self::Output {
            crate::modules::Add::new(self, rhs)
        }
    }
}

其中 type_params 是具有边界的类型参数的迭代器,#(#type_params),*, 创建这些类型参数的列表。