简体   繁体   English

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

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

I have the type Foo :我有Foo类型:

pub struct Foo { ... }

Now I want to create a procedural macro that creates an instance of this struct.现在我想创建一个过程宏来创建这个结构的一个实例。 This might involve heavy computation, file access, or other stuff only procedural macros can do, but the exact details of how to create that instance are not important here.这可能涉及繁重的计算、文件访问或其他只有过程宏才能做的事情,但如何创建该实例的确切细节在这里并不重要。

I defined my procedural macro like this:我这样定义我的程序宏:

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

    // TODO: return `foo_value`
}

The users of my procedural macros should be able to write this:我的程序宏的用户应该能够这样写:

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

Please note that Foo could contain a lot of data, like many megabytes of Vec data.请注意, Foo可能包含大量数据,例如数兆字节的Vec数据。

How can I return the Foo value from my procedural macro?如何从我的程序宏中返回Foo值?

While this seems like an easy request, there is actually a lot to unroll here.虽然这似乎是一个简单的请求,但实际上有很多事情要展开。

Most importantly, it is crucial to understand that procedural macros only return tokens (ie Rust code).最重要的是,了解过程宏只返回令牌(即 Rust 代码)至关重要。 To put it bluntly: the Rust compiler executes your procedural macro, takes the resulting tokens and pastes them in the users code where your procedural macro invocation was.坦率地说:Rust 编译器执行您的过程宏,获取生成的标记并将它们粘贴到您的过程宏调用所在的用户代码中。 You can think of procedural macros as a pre-processing step that takes your Rust code, transforms it and spits out another .rs file.您可以将过程宏视为一个预处理步骤,它将您的 Rust 代码转换为另一个.rs文件。 That file is then fed to the compiler.然后将该文件提供给编译器。


In order to "return a value of Foo " you have to return a TokenStream that represents an expression which evaluates to Foo .为了“返回Foo的值”,您必须返回一个TokenStream ,它代表一个计算结果为Foo的表达式。 For example:例如:

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

In the user's crate:在用户的箱子中:

let a: Foo = create_foo!();

Which would expand to:这将扩展为:

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

The data: vec,[1, 2, 3] part could be generated dynamically by the procedural macro. data: vec,[1, 2, 3]部分可以由过程宏动态生成。 If your Foo instance is very large, the code creating that instance is probably very large as well.如果您的Foo实例非常大,则创建该实例的代码也可能非常大。 This means that compile times might increase because the Rust compiler has to parse and check this huge expression.这意味着编译时间可能会增加,因为 Rust 编译器必须解析和检查这个巨大的表达式。


So you can't return the value directly?所以不能直接返回值? No. You might think that you could do it with unsafe code.不,你可能认为你可以使用unsafe的代码来做到这一点。 For example, emit a big const DATA: &[u8] =...;例如,发出一个大的const DATA: &[u8] =...; and mem::transmute it to Foo , but you can't for several reasons:mem::transmute它到Foo ,但你不能有几个原因:

  • The procedural macro and the user's crate might not run on the same platform (CPU, OS, ...) which all might influence how Foo is represented in memory.过程宏和用户的 crate 可能不在同一个平台(CPU、OS、...)上运行,这都可能影响Foo在 memory 中的表示方式。 The same Foo instance might be represented differently in memory for your procedural macro and your user crate, so you can't transmute .对于您的程序宏和您的用户 crate,相同的Foo实例在 memory 中的表示方式可能不同,因此您不能transmute
  • If Foo contains heap allocated structures ( Vec ), you can't do it anyway.如果Foo包含堆分配结构( Vec ),你无论如何都不能这样做。

If you must generate the value in your procedural macro, then there is only one solution to get it to the user, but this is not optimal.如果您必须在程序宏中生成值,那么只有一种解决方案可以将其提供给用户,但这并不是最优的。 Alternatively, maybe calculating it at runtime once isn't that bad.或者,也许在运行时计算一次并不是那么糟糕。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM