You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Cargo Run编译失败但Cargo Expand展开代码可运行的Const泛型Builder宏异常问题

Cargo Run编译失败但Cargo Expand展开代码可运行的Const泛型Builder宏异常问题

我之前也踩过proc_macro生成const泛型代码的类似坑!你的问题核心出在宏生成代码时的Span设置错误,导致编译器无法正确关联泛型参数与对应的impl实现。

问题场景复盘

你基于const泛型实现了一个编译时检查字段是否全部设置的Builder宏,结果cargo run时报错UserBuilder<false, false>找不到set_username方法,但把cargo expand后的代码直接复制到项目中运行却完全正常,这种"代码展开后就好使"的情况确实特别迷惑人。

错误原因分析

看你的宏代码,生成泛型参数(比如X_0USERNAME_CONFIGURED)时用了Span::def_site()

let const_name = syn::Ident::new(
    &format!("{}_CONFIGURED", to_const_case(&ident.to_string())),
    Span::def_site().into(), // 这里是问题根源
);

Span::def_site()指向的是宏定义所在的位置(也就是你的macros crate),而不是宏被调用的位置(你的业务crate)。这会导致编译器认为泛型参数的上下文不一致:

  • UserBuilder<false, false>的泛型参数Span属于你的业务crate
  • impl<const X_0: bool> UserBuilder<false, X_0>中的X_0 Span属于macros crate

这种上下文不匹配会让编译器无法将impl实现与UserBuilder<false, false>实例关联起来,自然就找不到对应的set_username方法。

cargo expand后的代码直接展现在你的业务crate中,所有代码的Span都属于同一上下文,编译器就能正常识别impl和结构体的关联关系。

解决办法

把生成Ident时的Span改成和宏调用位置一致的上下文即可:

  1. 对于字段对应的XXXXX_CONFIGURED泛型参数,直接复用原结构体字段的Span:
let const_name = syn::Ident::new(
    &format!("{}_CONFIGURED", to_const_case(&ident.to_string())),
    ident.span().into(), // 改用字段本身的Span(宏调用位置的Span)
);
  1. 对于临时泛型参数X_0X_1等,使用Span::call_site()
let new_ident = syn::Ident::new(
    &format!("X_{}", field),
    Span::call_site().into(), // 改用宏调用位置的Span
);

这样生成的所有泛型参数和impl代码的Span都和你的业务代码上下文一致,编译器就能正确匹配impl与UserBuilder的实例了。

额外验证点

你可以顺便确认下to_const_case函数是否正确将字段名转成大写(比如usernameUSERNAME),不过从cargo expand的结果来看这部分是正常的,所以核心问题肯定是Span的设置错误。


内容来源于stack exchange

火山引擎 最新活动