微软的一位大牛,在Github上开发了一个Rust模块,名为PyO3。这个模块是Python的Rust绑定,通过它可以实现Rust与Python的混合编程。在这个项目开始的时候,还有一个功能类似的模块叫做rust_cpython,在当时是名气很大的一个项目,后来,rust_cpython的开发者也加入了PyO3项目,PyO3项目吸收了rust_cpython后变得十分强大,它可以在Rust中调用Python的模块,以弥补Rust库的还不足的现实问题,也可以将Rust直接编译成Python的模块,实现为Python加速的目的。
项目的GitHub地址为:https://github.com/PyO3/pyo3。此外,围绕着这个项目,还有一系列周边项目:
- rust-numpy:使Rust可以调用numpy来方便的操作数据:https://github.com/PyO3/rust-numpy
- setuptools-rust:为Python的SetpuTools编写的Rust扩展,使Python可以安装Rust生成的模块:https://github.com/PyO3/setuptools-rust
- maturin:使用setuptools-rust将Rust扩展编译成Python模块还是有些麻烦的。这个库可以简化这个过程,实现0配置的生成Python的Rust扩展:https://github.com/PyO3/maturin
Rust调用Python
在没有进行实际的项目时,不太容易想象Rust在哪里需要调用Python来实现某些功能。这里先用PyO3中的例子来学习一下,知道有这种可能,将来遇到用Rust解决实际问题比较困难的时候,可以将此列为一个选项。
官方的例子是调用Python的sys模块来读取Python的版本号,和使用Python的os模块来读取环境变量:
- 首先使用cargo生成一个可执行的项目:
cargo new rust_python
- 在生成的Cargo.toml中的dependencies中添加pyo3依赖:
[dependencies]
pyo3=""
- 在生成的main.rs中的编写如下代码:
use pyo3::prelude::*;
use pyo3::types::IntoPyDict;
fn main() -> Result<(), ()> {
Python::with_gil(|py| {
main_(py).map_err(|e| {
// We can't display Python exceptions via std::fmt::Display,
// so print the error here manually.
e.print_and_set_sys_last_vars(py);
})
})
}
fn main_(py: Python) -> PyResult<()> {
// 导入sys模块
let sys = py.import("sys")?;
// 从调用sys.verson取得Python版本
let version: String = sys.get("version")?.extract()?;
// 将os模块导入本地变量字典
let locals = [("os", py.import("os")?)].into_py_dict(py);
// 以字符串的形式编写Python代码
let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
// 使用Python的eval函数执行Python代码
let user: String = py.eval(code, None, Some(&locals))?.extract()?;
println!("Hello {}, I'm Python {}", user, version);
Ok(())
}