繁体   English   中英

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

[英]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数据。

如何从我的程序宏中返回Foo值?

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

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


为了“返回Foo的值”,您必须返回一个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 编译器必须解析和检查这个巨大的表达式。


所以不能直接返回值? 不,你可能认为你可以使用unsafe的代码来做到这一点。 例如,发出一个大的const DATA: &[u8] =...; mem::transmute它到Foo ,但你不能有几个原因:

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

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

暂无
暂无

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

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