Macros 是否可以使用Rust宏按程序声明变量?

Macros 是否可以使用Rust宏按程序声明变量?,macros,rust,Macros,Rust,这个问题基本上分为两个部分: 能否将未知标识符传递给中的宏 能否在Rust宏中组合字符串以生成新变量名 例如,类似于: macro\u规则!扩展( ($x:ident)=>( 设mut x_ux=0; ) ) 呼叫扩展!(hi)明显失败,因为hi是未知标识符;但你能做到这一点吗 也就是说,C中的等价物类似于: #包括 #定义FN(名称、基础)\ int x1_35;#Name=0+基数\ int x2_35;#Name=2+基数\ int x3###Name=4+基数\ int x4###Na

这个问题基本上分为两个部分:

  • 能否将未知标识符传递给中的宏

  • 能否在Rust宏中组合字符串以生成新变量名

  • 例如,类似于:

    macro\u规则!扩展(
    ($x:ident)=>(
    设mut x_ux=0;
    )
    )
    
    呼叫扩展!(hi)明显失败,因为hi是未知标识符;但你能做到这一点吗

    也就是说,C中的等价物类似于:

    #包括
    #定义FN(名称、基础)\
    int x1_35;#Name=0+基数\
    int x2_35;#Name=2+基数\
    int x3###Name=4+基数\
    int x4###Name=8+基数\
    int x5_35;##Name=16+基数;
    int main(){
    FN(你好,10)
    printf(“%d%d%d%d%d\n”,x1\u你好,x2\u你好,x3\u你好,x4\u你好,x5\u你好);
    返回0;
    }
    
    为什么你说,这是个多么糟糕的主意。你为什么要这么做

    我很高兴你问我

    考虑一下这个锈块:

    {
    设标记=0;
    let borrowed=借用块的生命周期(数据和标记);
    不安全{
    执行ffi电话(借用);
    }
    }
    
    现在,您有一个具有显式限定生存期(标记)的借用值,该值不使用结构生存期,但我们可以保证它在ffi调用的整个范围内都存在;同时,我们不会遇到模糊的错误,其中
    *
    在不安全块中被不安全地取消引用,因此编译器不会将其作为错误捕获,尽管错误是在安全块中发生的

    (另见)


    使用一个可以声明临时变量的宏可以大大缓解我与编译器之间的矛盾。这就是我想这样做的原因。

    是的,但是这仅作为夜间实验API提供,可能会被删除

    您可以将任意标识符传递到宏中,是的,您可以使用宏将标识符连接到新标识符中:

    #![特征(浓度识别)]
    宏规则!试验{
    ($x:ident)=>({
    设z=concat_idents!(hello_uux,$x);
    z();
    })
    }
    fn你好{u world(){}
    fn main(){
    测试!(世界);
    }
    
    然而,据我所知,因为
    concat\idents!()
    本身是一个宏,您不能在任何地方使用这个串联标识符,您可以在任何地方使用普通标识符,只有在上面的示例中这样的某些地方,在我看来,这是一个巨大的缺点。就在昨天,我尝试编写一个宏,它可以删除代码中的许多样板文件,但最终我无法做到这一点,因为宏不支持连接标识符的任意放置

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

    macro\u规则!试验{
    ($body:expr)=>({let x=10;$body})
    }
    fn main(){
    设y=test!(x+10);
    println!(“{}”,y);
    }
    
    也就是说,我们创建一个变量
    x
    ,并在其声明后放置一个表达式。然后很自然地认为
    x
    测试中!(x+10)
    指的是宏声明的变量,一切正常,但实际上这段代码不会编译:

    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
    

    所以,若你们所需要的只是局部变量的唯一性,那个么你们可以放心地不做任何事情,使用任何你们想要的名字,它们将自动唯一。这是在宏教程中,尽管我发现其中的示例有些混乱。

    concat_idents
    不起作用的情况下(大多数情况下我都希望使用它),将问题从串联标识符改为使用名称空间确实有效

    也就是说,不是非工作代码:

    macro_rules! test {
        ($x:ident) => ({
            struct concat_idents!(hello_, $x) {}
            enum contact_idents!(hello_, $x) {}
        })
    }
    
    用户可以命名名称空间,然后使用预设名称,如下所示:

    macro_rules! test {
        ($x:ident) => ({
            mod $x {
                struct HelloStruct {}
                enum HelloEnum {}
            }
        })
    }
    
    现在您有了一个基于宏参数的名称。此技术仅在特定情况下有用。

    也有,在
    concat_dents
    动力不足或无法针对夜间编译器的情况下,此技术非常有效

    macro\u规则!富奥宏{
    ($($name:ident),+)=>{
    粘贴::项目{
    #[测试]
    fn[](){
    断言!错误
    }
    }
    };
    }
    
    如果您不想使用夜间和外部板条箱,并且您的标识符是类型,则可以将标识符收集到结构中

    使用std::fmt::Debug;
    fn打印(v:&T){
    println!(“{:?}”,v);
    }
    宏规则!全部打印{
    ($($name:ident),+)=>{
    结构值{
    $($name:$name)+
    }
    让值=值{
    $(
    $name:$name::default()
    ),+
    };
    $(
    打印(&value.$name);
    )+
    };
    }
    fn main(){
    打印所有!(字符串,i32,usize);
    }
    
    此代码打印

    ""
    0
    0
    
    如果您担心
    Value
    会与某个类型名称冲突,可以使用某个长UUID作为名称的一部分:

    struct Values_110cf51d7a694c808e6fe79bf1485d5b{
    $($name:$name)+
    }
    
    concat_ident可能会删除,它已经是功能门控的,我们真的需要类似于C中的##的功能。由于字符串concat非常有限,我经常需要编写用户给我两个名称的宏。但是说Rust宏变量不能泄漏是不公平的,因为Rust宏声明总是需要包装
    {}
    (或其他分隔符)。在C中使用相同的语法,您仍然不会泄漏符号。@请注意,上面的示例在C中也适用:。其思想是,在C中,只要知道宏定义,您就可以非常轻松地访问宏定义的变量。在Rust中,您不能这样做,并且是否有块与此无关。是否有方法生成很多功能