如何在类似函数的过程宏中计算类型的实例并 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::transmute
到 Foo
,但你不能这样做有几个原因:
- 程序宏和用户的 crate 可能不在同一平台上 运行(CPU、OS、...),这都可能影响
Foo
在内存中表示。相同的 Foo
实例在程序宏和用户 crate 中的内存表示可能不同,因此您不能 transmute
.
- 如果
Foo
包含堆分配结构(Vec
),你无论如何也做不到。
如果您必须在过程宏中生成值,那么只有一种解决方案可以将其提供给用户,但这不是最佳方案。或者,也许在 运行 时间计算一次也不错。
我的类型是 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::transmute
到 Foo
,但你不能这样做有几个原因:
- 程序宏和用户的 crate 可能不在同一平台上 运行(CPU、OS、...),这都可能影响
Foo
在内存中表示。相同的Foo
实例在程序宏和用户 crate 中的内存表示可能不同,因此您不能transmute
. - 如果
Foo
包含堆分配结构(Vec
),你无论如何也做不到。
如果您必须在过程宏中生成值,那么只有一种解决方案可以将其提供给用户,但这不是最佳方案。或者,也许在 运行 时间计算一次也不错。