是否可以使用 Rust 宏在程序上声明变量? [英] Is it possible to declare variables procedurally using Rust macros?

查看:19
本文介绍了是否可以使用 Rust 宏在程序上声明变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

基本上,这个问题有两个部分:

Basically, there are two parts to this question:

  1. 你能否将未知标识符传递给 Rust 中的宏?

你能在 Rust 宏中组合字符串来生成新的变量名吗?

Can you combine strings to generate new variable names in a Rust macro?

例如,类似于:

macro_rules! expand(
  ($x:ident) => (
    let mut x_$x = 0;
  )
)

调用 expand!(hi) 显然失败,因为 hi 是未知标识符;但你能以某种方式做到这一点吗?

Calling expand!(hi) obvious fails because hi is an unknown identifier; but can you somehow do this?

即.在 C 中的等价物如下:

ie. The equivalent in C of something like:

#include <stdio.h>
#define FN(Name, base) 
  int x1_##Name = 0 + base; 
  int x2_##Name = 2 + base; 
  int x3_##Name = 4 + base; 
  int x4_##Name = 8 + base; 
  int x5_##Name = 16 + base;

int main() {
  FN(hello, 10)
  printf("%d %d %d %d %d
", x1_hello, x2_hello, x3_hello, x4_hello, x5_hello);
  return 0;
}

为什么这么说,真是个糟糕的主意.你为什么要这样做?

Why you say, what a terrible idea. Why would you ever want to do that?

很高兴你问了!

考虑这个锈块:

{
   let marker = 0;
   let borrowed = borrow_with_block_lifetime(data, &marker); 
   unsafe {
      perform_ffi_call(borrowed);
   }
}

您现在有一个借用值,其生命周期(标记)明确有界,不使用结构生命周期,但我们可以保证在 ffi 调用的整个范围内都存在;同时,我们不会遇到模糊的错误,其中 * 在不安全块内被不安全地取消引用,因此编译器不会将其捕获为错误,尽管发生了错误 在安全块内.

You now have a borrowed value with an explicitly bounded lifetime (marker) that isn't using a structure lifetime, but that we can guarantee exists for the entire scope of the ffi call; at the same time we don't run into obscure errors where a * is de-referenced unsafely inside an unsafe block and so the compiler doesn't catch it as an error, despite the error being made inside a safe block.

(另见 为什么我的所有指针都指向与 rust 中的 to_c_str() 相同的地方?)

使用可以为此目的声明临时变量的宏将大大减轻我与编译器斗争的麻烦.这就是我想要这样做的原因.

The use a macro that can declare temporary variables for this purpose would considerably ease the troubles I have fighting with the compiler. That's why I want to do this.

推荐答案

是的,但是这仅作为夜间专用的实验性 API 可用,可能会被删除.

Yes however this is only available as a nightly-only experimental API which may be removed.

您可以将任意标识符传递给宏,是的,您可以使用 concat_idents!() 宏:

You can pass arbitrary identifier into a macro and yes, you can concatenate identifiers into a new identifier using concat_idents!() macro:

#![feature(concat_idents)]

macro_rules! test {
    ($x:ident) => ({
        let z = concat_idents!(hello_, $x);
        z();
    })
}

fn hello_world() {  }

fn main() {
    test!(world);
}

然而,据我所知,因为 concat_idents!() 本身是一个宏,你不能使用这个连接标识符无处不在你可以使用普通标识符,仅在某些地方,例如上面的示例,在我看来,这是一个巨大的缺点.就在昨天,我试图编写一个宏,它可以删除我代码中的大量样板,但最终我无法做到,因为宏不支持任意放置连接标识符.

However, as far as I know, because concat_idents!() itself is a macro, you can't use this concatenated identifier everywhere you could use plain identifier, only in certain places like in example above, and this, in my opinion, is a HUGE drawback. Just yesterday I tried to write a macro which could remove a lot of boilerplate in my code, but eventually I was not able to do it because macros do not support arbitrary placement of concatenated identifiers.

顺便说一句,如果我正确理解你的想法,你真的不需要连接标识符来获得唯一的名称.与 C 宏相反,Rust 宏是卫生.这意味着在宏中引入的所有局部变量的名称都不会泄漏到调用此宏的范围内.例如,您可以假设此代码有效:

BTW, if I understand your idea correctly, you don't really need concatenating identifiers to obtain unique names. Rust macros, contrary to the C ones, are hygienic. This means that all names of local variables introduced inside a macro won't leak to the scope where this macro is called. For example, you could assume that this code would work:

macro_rules! test {
    ($body:expr) => ({ let x = 10; $body })
}

fn main() {
    let y = test!(x + 10);
    println!("{}", y);
}

也就是说,我们创建一个变量 x 并在它的声明之后放置一个表达式.然后很自然地认为test!(x + 10)中的x指的是宏声明的那个变量,一切都应该没问题,但实际上这段代码不会编译:

That is, we create a variable x and put an expression after its declaration. It is then natural to think that x in test!(x + 10) refers to that variable declared by the macro, and everything should be fine, but in fact this code won't compile:

main3.rs:8:19: 8:20 error: unresolved name `x`.
main3.rs:8     let y = test!(x + 10);
                             ^
main3.rs:3:1: 5:2 note: in expansion of test!
main3.rs:8:13: 8:27 note: expansion site
error: aborting due to previous error

因此,如果您只需要本地人的唯一性,那么您可以安全地什么都不做并使用您想要的任何名称,它们将自动唯一.这是在宏教程中解释,尽管我发现那里的例子有点令人困惑.

So if all you need is uniqueness of locals, then you can safely do nothing and use any names you want, they will be unique automatically. This is explained in macro tutorial, though I find the example there somewhat confusing.

这篇关于是否可以使用 Rust 宏在程序上声明变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆