0

Rust 宏是否可以匹配封闭函数的返回类型?

一个示例类似于日志记录和断言宏,它也会在返回Err的函数中返回Result,而在未返回的函数中出现恐慌Result。为了实现这一点,宏应该以某种方式知道封闭函数的返回类型。

我想这对于声明性宏 ( ) 是不可能的,macro_rules!因为它们有一组有限的匹配类型(如Rust 参考,示例宏一章中所述):项目、块、语句、模式、表达式、类型、标识符等on,但不是封闭函数的返回类型。

但也许使用过程宏?

4

1 回答 1

1

摘要:不,即使使用过程宏也不容易。而且我实际上认为你不应该写这样的东西,即使它是可能的。只需让您的宏评估为 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 Errpanic!()取决于返回类型。即:通过过程宏为您自己的宏执行宏调用步骤。

我认为这是一个坏主意,原因有几个。最重要的是,我认为编译器调用过程宏时定义不明确。foo!()因此编译器可能会在调用过程宏之前尝试调用您的宏。

所以它可以通过程序宏工作,但不是以典型的方式工作。所以它相当hacky


我认为最好的解决方案

最后,我该怎么做?让您的宏评估为Result. 然后用户可以轻松地决定自己如何处理它。如果他们返回 a Result,他们只需要添加?. 如果他们不这样做,他们可以自由选择.unwrap()expect()以及其他恐慌方式。

我理解您为什么要尝试做自己想做的事情(这对用户来说更容易且舒适),但我认为这不是一个好主意。它可能归结为“远处的怪异动作”:函数中的宏突然做什么取决于该函数的返回类型。这意味着当你改变它时,函数的整个语义都会改变。这听起来像是你可以很容易地射中自己的脚。这也可能是 Rust 不容易的原因。

于 2019-01-24T12:53:24.783 回答