如何在类似函数的过程宏中计算类型的实例并 return 它?

How can I compute an instance of a type in a function-like procedural macro and return it?

我的类型是 Foo:

pub struct Foo { ... }

现在我想创建一个过程宏来创建该结构的一个实例。这可能涉及繁重的计算、文件访问或其他只有过程宏可以做的事情,但如何创建该实例的具体细节在这里并不重要。

我这样定义程序宏:

#[proc_macro]
pub fn create_foo(_: TokenStream) -> TokenStream {
    let foo_value: Foo = /* some complex computation */;

    // TODO: return `foo_value`
}

我的程序宏的用户应该可以这样写:

fn main() {
    let a: Foo = create_foo!();
}

请注意,Foo 可能包含 大量 数据,例如数兆字节的 Vec 数据。

如何 return 我的程序宏中的 Foo 值?

虽然这似乎是一个简单的请求,但实际上这里有很多内容需要展开。

最重要的是,了解过程宏仅 return tokens(即 Rust 代码)至关重要。坦率地说:Rust 编译器执行您的过程宏,获取生成的标记并将它们粘贴到您的过程宏调用所在的用户代码中。您可以将过程宏视为一个预处理步骤,它获取您的 Rust 代码,对其进行转换并生成另一个 .rs 文件。 那个文件然后被提供给编译器。


为了 "return a value of Foo" 你必须 return 一个 TokenStream 代表一个计算结果为 Foo 的表达式。例如:

#[proc_macro]
pub fn create_foo(_: TokenStream) -> TokenStream {
    quote! { Foo { data: vec![1, 2, 3] } }
}

在用户的箱子中:

let a: Foo = create_foo!();

这将扩展为:

let a: Foo = Foo { data: vec![1, 2, 3] };

data: vec![1, 2, 3] 部分可以由程序宏动态生成。如果您的 Foo 实例非常大,那么创建该实例的代码也可能非常大。这意味着编译时间可能会增加,因为 Rust 编译器必须解析和检查这个巨大的表达式。


所以你不能直接return这个值?不,您可能认为可以使用 unsafe 代码来完成。例如,发出一个大 const DATA: &[u8] = ...;mem::transmuteFoo,但你不能这样做有几个原因:

  • 程序宏和用户的 crate 可能不在同一平台上 运行(CPU、OS、...),这都可能影响 Foo在内存中表示。相同的 Foo 实例在程序宏和用户 crate 中的内存表示可能不同,因此您不能 transmute.
  • 如果Foo包含堆分配结构(Vec),你无论如何也做不到。

如果您必须在过程宏中生成值,那么只有一种解决方案可以将其提供给用户,但这不是最佳方案。或者,也许在 运行 时间计算一次也不错。