天天看點

在 Rust 代碼中編寫 Python 是種怎樣的體驗?

本文為轉載摘要,完整版請移步:

​​https://mp.weixin.qq.com/s/YT_HNFDCQ_IyocvBkRNJnA​​

以下為譯文:

大約一年前,我釋出了一個名為inline-python(https://crates.io/crates/inline-python)的Rust類庫,它允許大家使用python!{

.. }宏輕松地将一些Python混合到Rust代碼中。在本系列中,我将從頭展示開發此類庫的過程。

-1-

預覽

如果不熟悉inline-python類庫,你可以執行以下操作:

fn main() {
    let who = "world";
    let n = 5;
    python! {
        for i in range('n):
            print(i, "Hello", 'who)
        print("Goodbye")
    }
}      

它允許你将Python代碼直接嵌入Rust代碼行之間,甚至直接在Python代碼中使用Rust變量。

我們将從一個比這個簡單得多的案例開始,然後逐漸努力以達到這個結果(甚至更多!)。

-2-

運作Python代碼

首先,讓我們看一下如何在Rust中運作Python代碼。讓我們嘗試使第一個簡單的示例生效:

fn main() {
    println!("Hello ...");
    run_python("print(\"... World!\")");
}      

我們可以使用std::process::指令來運作python可執行檔案并傳遞python代碼,進而實作run_python,但如果我們希望能夠定義和讀回Python變量,那麼最好從使用PyO3庫開始。

PyO3為我們提供了Python的Rust綁定。它很好地包裝了Python C API,使我們可以直接在Rust中與各種Python對象互動。(甚至在Rust中編寫Python庫,但這是另一個主題。)

它的Python::run 功能完全符合我們的需求。它将Python代碼作為&str,并允許我們使用兩個可選的PyDicts 來定義範圍内的任何變量。讓我們試一試吧:

fn run_python(code: &str) {
    let py = pyo3::Python::acquire_gil(); // Acquire the 'global interpreter lock', as Python is not thread-safe.
    py.python().run(code, None, None).unwrap(); // No locals, no globals.
}      
$ cargo run
   Compiling scratchpad v0.1.0
    Finished dev [unoptimized + debuginfo] target(s) in 0.29s
     Running `target/debug/scratchpad`
Hello ...
... World!      

看,這就成功了!

-3-

基于規則的宏

在字元串中編寫Python不是最便捷的方法,是以我們嘗試改進它。宏允許我們在Rust中自定義文法,是以讓我們嘗試一下:

fn main() {
    println!("Hello ...");
    python! {
        print("... World!")
    }
}