是否有方法将参数与Rust宏匹配?

是否有方法将参数与Rust宏匹配?,rust,macros,match,guard,Rust,Macros,Match,Guard,考虑以下代码: 特质{ fn x&self->u32; } 结构A{} 一种动物的简单特征{ fn x&self->u32{ 10 } } 结构B{} B的impl特征{ fn x&self->u32{ 20 } } 结构C{ 创建时间:u64, } C的impl特征{ fn x&self->u32{ 30 } } impl C{ 发布fn新->C{ C{created_time:1000}//为了简单起见 } } 宏规则!创造{ $type:ident=>{ 匹配stringify!$type

考虑以下代码:

特质{ fn x&self->u32; } 结构A{} 一种动物的简单特征{ fn x&self->u32{ 10 } } 结构B{} B的impl特征{ fn x&self->u32{ 20 } } 结构C{ 创建时间:u64, } C的impl特征{ fn x&self->u32{ 30 } } impl C{ 发布fn新->C{ C{created_time:1000}//为了简单起见 } } 宏规则!创造{ $type:ident=>{ 匹配stringify!$type{ C=>Box::newC::newas-Box, _=>Box::新的$type{}作为Box, } }; } fn干线{ 设a:Box=create!a; 设b:Box=create!b; 设c:Box=create!c; 断言式a.x,10; 断言式b.x,20; 断言式c.x,30; } 如果要求编译器展开宏,则解析为:

a:盒子= 匹配{ C=>Box::newC::newas-Box, _=>Box::newA{}as Box, }; b:盒子= 比赛B{ C=>Box::newC::newas-Box, _=>Box::newB{}作为Box, }; 让c:盒子= 比赛C{ C=>Box::newC::newas-Box, _=>Box::newC{}作为Box, }; 这很好地解释了为什么编译器在尝试编译时会出现以下错误:

error[E0063]: missing field `created_time` in initializer of `C`
  --> mwe.rs:29:27
   |
29 |             _ => Box::new($type { }) as Box<dyn T>,
   |                           ^^^^^ missing `created_time`
...
37 |     let c: Box<dyn T> = create!(C);
   |                         ---------- in this macro invocation

error: aborting due to previous error
导致

让c:盒子= 如果C==C{Box::newC::newas Box} else{Box::newC{}as Box}; 与匹配尝试的错误相同

希望Haskell的guard pipe语法能在Rust中起作用,我最后还尝试了以下方法:

宏规则!创造{ $type:ident | stringify!$type==C=>{ 框::newC::新建为框 }, $type:ident | stringify!$type!=C=>{ Box::新的$type{}作为Box }, } 但这给出了一个错误:没有规则需要标记“|”

这最终让我回到标题中的问题:


有没有一种方法可以向宏规则中添加保护,告诉编译器只有在传递此参数时才运行a,或者在其他情况下运行B?

虽然您的问题看起来确实是X/Y问题,使用诸如Default之类的特性可以更优雅地解决,但在某种程度上可以匹配宏参数

您的宏可以重新编写为

宏规则!创造{ C=>{ 框::newC::新建为框 }; $type:ident=>{ Box::新的$type{}作为Box }; } 编译器在第一次成功匹配时停止

请注意,这有一些限制:正如您所期望的,编译器会对标记进行文字比较,例如,以下内容将失败:

D型=C型; 让我们再做一次吧!c:Box=create!D 与

错误[E0063]:在'C'的初始值设定项中缺少字段'created_time'` ->src/main.rs:41:18 | 41 | Box::新的$type{}as Box |^^^^^缺少“创建时间”` ... 51 |让c:Box=create!D |----在此宏调用中
虽然看起来您的问题是X/Y问题,使用诸如Default之类的特性可以更优雅地解决,但在某种程度上可以匹配宏参数

您的宏可以重新编写为

宏规则!创造{ C=>{ 框::newC::新建为框 }; $type:ident=>{ Box::新的$type{}作为Box }; } 编译器在第一次成功匹配时停止

请注意,这有一些限制:正如您所期望的,编译器会对标记进行文字比较,例如,以下内容将失败:

D型=C型; 让我们再做一次吧!c:Box=create!D 与

错误[E0063]:在'C'的初始值设定项中缺少字段'created_time'` ->src/main.rs:41:18 | 41 | Box::新的$type{}as Box |^^^^^缺少“创建时间”` ... 51 |让c:Box=create!D |----在此宏调用中
这似乎是一个错误。你想用这些来完成什么?在我看来,最简单的方法是将新方法添加到所有3种类型中,并始终使用它。还有你的声明:遗憾的是,它没有这样做,而是抱怨第二个不可能的子句无法编译。这是不正确的。Rust无法在编译时确定是否将采用某些ARM/分支,并且这些ARM中的代码必须在语法上仍然正确,即使它们从来都不正确called@sshashank124的确我想避免为不需要它的结构创建新函数,因为它没有字段或它们都是公共的,所以我尝试创建一个helper宏,但我想那是不可能的。是的,我个人认为一个新函数实际上不需要超过2-3行,并且在Rust中是普遍理解的。我不确定用不是每个人都熟悉的宏包装器替换它是否一定更好。同样,我个人的观点是,您还可以为您的结构派生默认特性,这将为您带来更少代码的好处,同时也为您提供了一种在不使用ar的情况下实例化实例的统一方法
guments@mcarton奥巴马的答案可能是最有效的。然而,像这样设置似乎是一种防锈模式。为什么不创建一个宏,根据需要将模板应用于每个类。这看起来像是一个错误。你想用这些来完成什么?在我看来,最简单的方法是将新方法添加到所有3种类型中,并始终使用它。还有你的声明:遗憾的是,它没有这样做,而是抱怨第二个不可能的子句无法编译。这是不正确的。Rust无法在编译时确定是否将采用某些ARM/分支,并且这些ARM中的代码必须在语法上仍然正确,即使它们从来都不正确called@sshashank124的确我想避免为不需要它的结构创建新函数,因为它没有字段或它们都是公共的,所以我尝试创建一个helper宏,但我想那是不可能的。是的,我个人认为一个新函数实际上不需要超过2-3行,并且在Rust中是普遍理解的。我不确定用不是每个人都熟悉的宏包装器替换它是否一定更好。同样,我个人的观点是,您还可以为您的结构派生默认特性,这将为您带来更少代码的好处,并且提供一种统一的方法来实例化实例,而无需使用arguments@mcarton奥巴马的答案可能是最有效的。然而,像这样设置似乎是一种防锈模式。为什么不创建一个宏,根据需要为每个类应用一个模板呢?虽然这并不是问题的完美解决方案,但它确实给出了问题的完美答案。谢谢。虽然这确实不是问题的完美解决方案,但它确实给出了问题的完美答案。谢谢