Rust 宏是否可以匹配封闭函数的返回类型?
一个示例类似于日志记录和断言宏,它也会在返回Err
的函数中返回Result
,而在未返回的函数中出现恐慌Result
。为了实现这一点,宏应该以某种方式知道封闭函数的返回类型。
我想这对于声明性宏 ( ) 是不可能的,macro_rules!
因为它们有一组有限的匹配类型(如Rust 参考,示例宏一章中所述):项目、块、语句、模式、表达式、类型、标识符等on,但不是封闭函数的返回类型。
但也许使用过程宏?
Rust 宏是否可以匹配封闭函数的返回类型?
一个示例类似于日志记录和断言宏,它也会在返回Err
的函数中返回Result
,而在未返回的函数中出现恐慌Result
。为了实现这一点,宏应该以某种方式知道封闭函数的返回类型。
我想这对于声明性宏 ( ) 是不可能的,macro_rules!
因为它们有一组有限的匹配类型(如Rust 参考,示例宏一章中所述):项目、块、语句、模式、表达式、类型、标识符等on,但不是封闭函数的返回类型。
但也许使用过程宏?
摘要:不,即使使用过程宏也不容易。而且我实际上认为你不应该写这样的东西,即使它是可能的。只需让您的宏评估为 aResult
并让用户处理它。
Rust 宏,程序性和声明性,只能访问它们的输入流:只是一个标记列表。对于类似函数的宏(您通过 调用的宏foo!(...)
),输入就是您传递给它们的内容。所以你可以手动传递返回类型:
macro_rules! foo {
(Result $($stuff:tt)*) => { return Err(()); };
($($stuff:tt)*) => { panic!(); };
}
fn returns_result() -> Result<String, ()> {
foo!(Result<(), ()>); // will return `Err`
Ok("hi".into())
}
fn returns_string() -> String {
foo!(String); // will panic
"hi".into()
}
但我想这不是您想要的:用户必须手动指定每个宏调用的返回类型。
以这种方式调用的过程宏也是如此。
我们可以定义一个函数的返回类型在输入令牌流中的过程宏吗?是的,最好通过 proc-macro 属性。如果你定义了这样一个属性bar
,你可以这样写:
#[bar]
fn returns_result() -> Result<String, ()> { ... }
并且您的过程宏将接收整个函数定义作为输入,包括返回类型。但是你打算如何处理这些信息?
您可以根据需要更改整个函数,因此一种想法是搜索foo!()
函数中的所有宏调用,并将它们替换为return Err
或panic!()
取决于返回类型。即:通过过程宏为您自己的宏执行宏调用步骤。
但我认为这是一个坏主意,原因有几个。最重要的是,我认为编译器调用过程宏时定义不明确。foo!()
因此编译器可能会在调用过程宏之前尝试调用您的宏。
所以它可以通过程序宏工作,但不是以典型的方式工作。所以它相当hacky。
最后,我该怎么做?让您的宏评估为Result
. 然后用户可以轻松地决定自己如何处理它。如果他们返回 a Result
,他们只需要添加?
. 如果他们不这样做,他们可以自由选择.unwrap()
,expect()
以及其他恐慌方式。
我理解您为什么要尝试做自己想做的事情(这对用户来说更容易且舒适),但我认为这不是一个好主意。它可能归结为“远处的怪异动作”:函数中的宏突然做什么取决于该函数的返回类型。这意味着当你改变它时,函数的整个语义都会改变。这听起来像是你可以很容易地射中自己的脚。这也可能是 Rust 不容易的原因。