与封闭函数的返回类型匹配

时间:2019-01-24 12:33:10

标签: macros rust return-type rust-macros

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

一个例子是类似日志记录和断言宏,它在返回Err的函数中也返回Result,而在不返回Result的函数中则出现紧急情况。要实现此功能,宏应该以某种方式知道封闭函数的返回类型。

我想声明性宏(macro_rules!)不可能做到这一点,因为它们的匹配类型有限(如The Rust Reference, chapter Macros By Example中所述):项,块,语句,模式,表达式,类型,标识符等,但不是封闭函数的返回类型。

但是也许带有程序宏?

1 个答案:

答案 0 :(得分:1)

摘要:不,即使使用程序宏也不容易。而且我实际上认为您不应该写这样的事情,即使有可能。只需让您的宏评估为Result并让用户处理它即可。


类似函数的宏

程序性和声明性的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!()宏。

因此,它可以通过过程宏来工作,但不能以典型的方式工作。因此,很黑


我认为最好的解决方案

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

我了解您为什么要尝试做自己想做的事情(这对用户来说更容易且舒适),但是我认为这不是一个好主意。它可能归结为“遥远的鬼动作” :您的函数中的宏突然执行的操作取决于该函数的返回类型。这意味着当您进行更改时,函数的整个语义都会发生变化。这听起来像是您可以非常轻松地将自己拍到脚上的东西。这可能也是在Rust中不容易的原因。