虽然这似乎是一个简单的请求,但实际上有很多事情要展开。
最重要的是,了解过程宏仅返回标记(即 Rust 代码)至关重要。坦率地说:Rust 编译器执行您的过程宏,获取生成的标记并将它们粘贴到您的过程宏调用所在的用户代码中。您可以将过程宏视为一个预处理步骤,它获取您的 Rust 代码,对其进行转换并输出另一个.rs
文件。然后将该文件提供给编译器。
为了“返回值Foo
”,您必须返回TokenStream
表示计算结果为的表达式的 a 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 编译器必须解析和检查这个巨大的表达式。
所以不能直接返回值?不,你可能认为你可以用unsafe
代码来做到这一点。例如,发出一个 bigconst DATA: &[u8] = ...;
和mem::transmute
它到Foo
,但你不能有几个原因:
- 程序宏和用户的 crate 可能不会在同一平台(CPU、OS、...)上运行,这都可能影响
Foo
在内存中的表示方式。对于您的程序宏和您的用户 crate,相同的Foo
实例在内存中的表示方式可能不同,因此您不能 transmute
.
- 如果
Foo
包含堆分配结构 ( Vec
),则无论如何您都不能这样做。
如果您必须在程序宏中生成值,那么只有一种解决方案可以将其提供给用户,但这并不是最优的。或者,也许在运行时计算一次并不是那么糟糕。