大家好今天要來試試看用 FFI 來接 Python 回傳的資料,不過筆者其實也不確定能不能成功,因為官網給的說明也很少不過沒關係我們還是可以嘗試看看,那麼先來介紹一下FFI。
其顧名思義就是和不同的程式語言溝通的介面,例如 C/C++ 等等因此我們可以這樣寫,
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
呼叫 C 的函式並且得到運算後的結果,但是要注意的因為 Rust 不能檢查其他程式是否安全所以都必須要放在 unsafe 的 block 裡面才可以運作。
而 Rust 也可以創建一個接口讓其他的程式呼叫他不過這不是今天的主題,有興趣的人可以到下面的參考連結。
筆者搜尋之後找到 PyO3 這個 library 而他提供可以讓 python 使用 Rust 函式做運算反之亦然或許這就是我們要的,來試試吧!
因為這個依賴只支援 Rust nightly 的版本所以我們必須先安裝並且把它設定為 default
$ rustup toolchain install nightly
$ rustup default nightly
接著再來新增依賴,
[dependencies]
pyo3 = "0.8.0"
然後我們直接照著他的範例試試看可不可以跑得起來,
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
fn main() -> PyResult<()> {
let gil = Python::acquire_gil();
let py = gil.python();
let sys = py.import("sys")?;
let version: String = sys.get("version")?.extract()?;
let locals = [("os", py.import("os")?)].into_py_dict(py);
let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
let user: String = py.eval(code, None, Some(&locals))?.extract()?;
println!("Hello {}, I'm Python {}", user, version);
Ok(())
}
這段程式碼很簡單基本上就是從 Rust 裡面調用 Python 的程式然後取得一些資訊。
執行結果看起來是 python 少了一些 library 還有環境變數的問題,後來改成用pyenv解決了。(真是好東西!)
成功的執行結果,
接下來我們可以試試看直接把翻譯的程式 import 進來,首先把 python 程式全部複製到 src 裡面,
]
接著我們需要稍微修改一下 python 的程式
檔案名稱:run_nn.py
def translate(original_sentence):
# preprocessing the original_sentence
init_jieba_dict()
sentence = seg2words(original_sentence)
sentence = preprocess_ch_sentence(sentence)
...
把原本用 argv 傳的參數改成函式的參數,並且把最下面 main 的判斷式都拿掉
if __name__ == '__main__':
# if len(sys.argv) != 2 or sys.argv[1] not in ['train','translate']:
# raise ValueError("""usage: python run_nn.py [train/translate]""")
if sys.argv[1] == 'train':
train()
if sys.argv[1] == 'translate':
translate()
這段全部刪除。
接著來改寫剛剛的 rust 程式,
fn main() -> PyResult<()> {
let gil = Python::acquire_gil();
let py = gil.python();
let run_nn = py.import("./python-lib/run_nn")?;
let result = run_nn.translate("你好");
// let sys = py.import("sys")?;
// let version: String = sys.get("version")?.extract()?;
// let locals = [("os", py.import("os")?)].into_py_dict(py);
// let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
// let user: String = py.eval(code, None, Some(&locals))?.extract()?;
println!("{}", result);
Ok(())
}
躍躍欲試了嗎?執行結果,
可能要讓各位失望了本次實驗結果宣告失敗~這個 library 目前無法支援比較複雜的程式詳情請參考這個 issue 和這篇說明。
雖然今天的目標沒有達成不過未來確實是有機會的,雖然不能用 FFI 的方式呼叫 Python 至少我們還是可以透過 Cli 沒有問題。接下來會回到我們的主題 Rust 跟 Webassembly 不過筆者還需要多一天的時間準備讀書會,所以明天會繼續開發跟研究 Actix 的潛在功能~
那麼我們明天見~