You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust过程宏编译时类型相等检查报错求助

问题排查:Rust过程宏funny_if编译错误

错误现象

使用过程宏时出现编译错误:

error: macro expansion ignores token `{` and any following
  --> src\event_listener\macros.rs:25:13
   |
25 | /             funny_if!{
26 | |                 (),
27 | |                 (),
28 | |                 {
...  |
33 | |                 }
34 | |             }
   | |             ^ caused by the macro expansion here
   | |_____________
   |
   = note: the usage of `funny_if!` is likely invalid in item context

当前实现代码

struct FunnyIf {
    type_to_match: Type,
    input_type: Type,
    true_block: Block,
    false_block: Block,
}

impl Parse for FunnyIf {
    fn parse(input: ParseStream) -> Result<Self> {
        let type_to_match: Type = input.parse()?;
        input.parse::<Token![,]>()?;
        let input_type: Type = input.parse()?;
        input.parse::<Token![,]>()?;
        let true_block: Block = input.parse()?;
        input.parse::<Token![,]>()?;
        let false_block: Block = input.parse()?;
        Ok(FunnyIf {
            type_to_match,
            input_type,
            true_block,
            false_block,
        })
    }
}

/// Creates a compile time if statement on types
#[proc_macro]
pub fn funny_if(input: TokenStream) -> TokenStream {
    let FunnyIf {
        type_to_match,
        input_type,
        true_block,
        false_block,
    } = parse_macro_input!(input as FunnyIf);
    let used_block = if type_to_match.to_token_stream().to_string()
        == input_type.to_token_stream().to_string()
    {
        true_block
    } else {
        false_block
    };
    let expanded = quote! {{
        #used_block
    }};
    expanded.into()
}

期望用法

macro_rules! example {
    ($typ:ty) => {
        funny_if! {
            (),
            $typ,
            { println!("type was ()"); },
            { println!("type wasn't ()"); }
        }
    }
}
fn main() {
    example!(()); // should print "type was ()"
    example!(u8); // should print "type wasn't ()"
}

问题原因与修复方案

1. 类型比较逻辑不可靠

当前通过to_token_stream().to_string()比较类型,会因空格、格式差异导致误判(比如Vec<i32>和Vec< i32 >字符串不同,但实际是同一类型)。syn::Type已实现PartialEq,直接用结构比较即可。

2. 宏展开生成冗余嵌套块

quote! {{ #used_block }}会生成双重花括号,若used_block本身已是块结构,会造成不必要的嵌套,直接返回选中的块即可。

3. 调用上下文错误

错误提示中的invalid in item context说明宏可能被用在item上下文(如模块顶层、函数外部),但该宏展开后是表达式,仅允许在表达式上下文(如函数内部)使用。

修复后的代码

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, parse::Parse, ParseStream, Token, Type, Block};

struct FunnyIf {
    type_to_match: Type,
    input_type: Type,
    true_block: Block,
    false_block: Block,
}

impl Parse for FunnyIf {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let type_to_match: Type = input.parse()?;
        input.parse::<Token![,]>()?;
        let input_type: Type = input.parse()?;
        input.parse::<Token![,]>()?;
        let true_block: Block = input.parse()?;
        input.parse::<Token![,]>()?;
        let false_block: Block = input.parse()?;
        Ok(FunnyIf {
            type_to_match,
            input_type,
            true_block,
            false_block,
        })
    }
}

/// Creates a compile time if statement on types
#[proc_macro]
pub fn funny_if(input: TokenStream) -> TokenStream {
    let FunnyIf {
        type_to_match,
        input_type,
        true_block,
        false_block,
    } = parse_macro_input!(input as FunnyIf);
    
    // 直接比较类型结构,避免字符串匹配的误差
    let used_block = if type_to_match == input_type {
        true_block
    } else {
        false_block
    };
    
    // 直接返回选中的代码块,无需额外嵌套
    quote!(#used_block).into()
}

验证修复效果

确保宏调用位于函数等表达式上下文:

macro_rules! example {
    ($typ:ty) => {
        funny_if! {
            (),
            $typ,
            { println!("type was ()"); },
            { println!("type wasn't ()"); }
        }
    }
}
fn main() {
    example!(()); // 输出 "type was ()"
    example!(u8); // 输出 "type wasn't ()"
}

内容的提问来源于stack exchange,提问作者Yavor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 00:38:23