簡體   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