本章介紹 Maturin,Rust Python 套件的建構工具。

本章目標

學完本章後,你將能夠:

  1. 設定 Maturin 專案
  2. 使用 maturin develop 快速迭代
  3. 建構跨平台 wheel 並發布到 PyPI

【原理層】Maturin 是什麼?

建構工具的角色

 1傳統 Python 擴展建構:
 2├── setuptools + setup.py
 3├── 複雜的編譯器設定
 4├── 手動處理連結問題
 5└── 跨平台建構困難
 6
 7Maturin 提供:
 8├── 一鍵建構 Rust Python 套件
 9├── 自動處理 PyO3 設定
10├── 跨平台 wheel 生成
11├── 與 pyproject.toml 整合
12└── 開發模式快速迭代

支援的專案類型

1Maturin 支援:
2├── pyo3:純 Rust 擴展(最常用)
3├── cffi:生成 cffi 綁定
4├── uniffi:跨語言綁定
5└── bin:Rust 二進位程式打包

【設計層】專案設定

安裝 Maturin

 1# 使用 pip
 2pip install maturin
 3
 4# 使用 pipx(推薦,隔離環境)
 5pipx install maturin
 6
 7# 使用 cargo
 8cargo install maturin
 9
10# 驗證安裝
11maturin --version

建立新專案

 1# 互動式建立
 2maturin new my_rust_module
 3cd my_rust_module
 4
 5# 或者指定綁定類型
 6maturin new my_rust_module --bindings pyo3
 7
 8# 專案結構
 9my_rust_module/
10├── Cargo.toml
11├── pyproject.toml
12├── src/
13│   └── lib.rs
14└── python/
15    └── my_rust_module/
16        └── __init__.py  # 選用

pyproject.toml 設定

 1[build-system]
 2requires = ["maturin>=1.5,<2.0"]
 3build-backend = "maturin"
 4
 5[project]
 6name = "my-rust-module"
 7version = "0.1.0"
 8description = "A Python module written in Rust"
 9requires-python = ">=3.8"
10classifiers = [
11    "Programming Language :: Rust",
12    "Programming Language :: Python :: Implementation :: CPython",
13    "Programming Language :: Python :: Implementation :: PyPy",
14]
15
16[project.optional-dependencies]
17dev = ["pytest", "hypothesis"]
18
19[tool.maturin]
20# 模組名稱(如果與 crate 名稱不同)
21module-name = "my_rust_module"
22
23# Python 原始碼目錄(如果有純 Python 程式碼)
24python-source = "python"
25
26# 功能標誌
27features = ["pyo3/extension-module"]
28
29# 啟用 abi3
30# features = ["pyo3/extension-module", "pyo3/abi3-py38"]
31
32# 排除不需要的檔案
33exclude = ["tests/*", "benches/*"]
34
35# 建構設定
36# strip = true  # 移除除錯符號(減小檔案大小)

Cargo.toml 設定

 1[package]
 2name = "my_rust_module"
 3version = "0.1.0"
 4edition = "2021"
 5
 6[lib]
 7name = "my_rust_module"
 8crate-type = ["cdylib"]
 9
10[dependencies]
11pyo3 = { version = "0.23", features = ["extension-module"] }
12
13[profile.release]
14lto = true          # Link-Time Optimization
15codegen-units = 1   # 更好的優化
16strip = true        # 移除除錯符號
17
18[profile.dev]
19opt-level = 0       # 快速編譯

【實作層】開發流程

maturin develop

 1# 開發模式:編譯並安裝到當前虛擬環境
 2maturin develop
 3
 4# 使用 release 模式(較慢但更快的執行時效能)
 5maturin develop --release
 6
 7# 指定功能
 8maturin develop --features "some-feature"
 9
10# 在其他虛擬環境中安裝
11maturin develop --target-dir target -E /path/to/venv
12
13# 使用後立即測試
14maturin develop && python -c "import my_rust_module; print(my_rust_module.add(1, 2))"

完整開發循環

 1# 1. 建立虛擬環境
 2python -m venv .venv
 3source .venv/bin/activate  # Linux/macOS
 4# .venv\Scripts\activate   # Windows
 5
 6# 2. 安裝開發依賴
 7pip install maturin pytest
 8
 9# 3. 開發循環
10while editing:
11    # 編輯 Rust 程式碼
12    vim src/lib.rs
13
14    # 建構並安裝
15    maturin develop
16
17    # 執行測試
18    pytest tests/
19
20# 4. 準備發布
21maturin build --release

混合 Python/Rust 專案

 1專案結構:
 2my_package/
 3├── Cargo.toml
 4├── pyproject.toml
 5├── src/
 6│   └── lib.rs              # Rust 程式碼
 7└── python/
 8    └── my_package/
 9        ├── __init__.py     # 匯入 Rust 模組
10        ├── utils.py        # 純 Python 程式碼
11        └── py.typed        # 型別標記
1# python/my_package/__init__.py
2from .my_package import *  # 從 Rust 模組匯入
3from .utils import helper_function  # 純 Python
4
5__version__ = "0.1.0"
1# pyproject.toml
2[tool.maturin]
3python-source = "python"
4module-name = "my_package.my_package"

【實作層】建構與發布

maturin build

 1# 建構 wheel
 2maturin build
 3
 4# Release 模式
 5maturin build --release
 6
 7# 指定 Python 版本
 8maturin build --interpreter python3.11
 9
10# 多個 Python 版本
11maturin build --interpreter python3.10 python3.11 python3.12
12
13# 使用 abi3(穩定 ABI)
14maturin build --release --features pyo3/abi3-py38
15
16# 建構結果位置
17ls target/wheels/
18# my_rust_module-0.1.0-cp311-cp311-linux_x86_64.whl

跨平台建構

 1# 使用 Docker 建構 manylinux wheel
 2maturin build --release --manylinux 2014
 3
 4# 常用的 manylinux 版本
 5# manylinux1:   CentOS 5 (非常老舊)
 6# manylinux2010: CentOS 6
 7# manylinux2014: CentOS 7 (推薦)
 8# manylinux_2_28: Debian 9 / Ubuntu 18.04
 9
10# 使用 zig 進行交叉編譯
11pip install ziglang
12maturin build --release --target x86_64-unknown-linux-gnu --zig
13
14# 常見目標平台
15# x86_64-unknown-linux-gnu
16# aarch64-unknown-linux-gnu
17# x86_64-apple-darwin
18# aarch64-apple-darwin
19# x86_64-pc-windows-msvc

發布到 PyPI

 1# 發布到 TestPyPI(測試)
 2maturin publish --repository testpypi
 3
 4# 發布到 PyPI
 5maturin publish
 6
 7# 使用 API token
 8maturin publish --username __token__ --password <your-pypi-token>
 9
10# 或者設定環境變數
11export MATURIN_PYPI_TOKEN=<your-pypi-token>
12maturin publish

【實作層】CI/CD 整合

GitHub Actions

  1# .github/workflows/build.yml
  2name: Build and Publish
  3
  4on:
  5  push:
  6    tags:
  7      - 'v*'
  8  pull_request:
  9    branches:
 10      - main
 11
 12jobs:
 13  # Linux
 14  linux:
 15    runs-on: ubuntu-latest
 16    strategy:
 17      matrix:
 18        target: [x86_64, aarch64]
 19    steps:
 20      - uses: actions/checkout@v4
 21
 22      - uses: actions/setup-python@v5
 23        with:
 24          python-version: '3.11'
 25
 26      - name: Build wheels
 27        uses: PyO3/maturin-action@v1
 28        with:
 29          target: ${{ matrix.target }}
 30          args: --release --out dist
 31          manylinux: auto
 32
 33      - name: Upload wheels
 34        uses: actions/upload-artifact@v4
 35        with:
 36          name: wheels-linux-${{ matrix.target }}
 37          path: dist
 38
 39  # macOS
 40  macos:
 41    runs-on: macos-latest
 42    strategy:
 43      matrix:
 44        target: [x86_64, aarch64]
 45    steps:
 46      - uses: actions/checkout@v4
 47
 48      - uses: actions/setup-python@v5
 49        with:
 50          python-version: '3.11'
 51
 52      - name: Build wheels
 53        uses: PyO3/maturin-action@v1
 54        with:
 55          target: ${{ matrix.target }}-apple-darwin
 56          args: --release --out dist
 57
 58      - name: Upload wheels
 59        uses: actions/upload-artifact@v4
 60        with:
 61          name: wheels-macos-${{ matrix.target }}
 62          path: dist
 63
 64  # Windows
 65  windows:
 66    runs-on: windows-latest
 67    steps:
 68      - uses: actions/checkout@v4
 69
 70      - uses: actions/setup-python@v5
 71        with:
 72          python-version: '3.11'
 73
 74      - name: Build wheels
 75        uses: PyO3/maturin-action@v1
 76        with:
 77          args: --release --out dist
 78
 79      - name: Upload wheels
 80        uses: actions/upload-artifact@v4
 81        with:
 82          name: wheels-windows
 83          path: dist
 84
 85  # 發布
 86  publish:
 87    needs: [linux, macos, windows]
 88    runs-on: ubuntu-latest
 89    if: startsWith(github.ref, 'refs/tags/')
 90    steps:
 91      - uses: actions/download-artifact@v4
 92        with:
 93          pattern: wheels-*
 94          merge-multiple: true
 95          path: dist
 96
 97      - name: Publish to PyPI
 98        uses: PyO3/maturin-action@v1
 99        env:
100          MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
101        with:
102          command: upload
103          args: --non-interactive --skip-existing dist/*

測試工作流程

 1# .github/workflows/test.yml
 2name: Test
 3
 4on:
 5  push:
 6    branches: [main]
 7  pull_request:
 8
 9jobs:
10  test:
11    runs-on: ${{ matrix.os }}
12    strategy:
13      matrix:
14        os: [ubuntu-latest, macos-latest, windows-latest]
15        python-version: ['3.9', '3.10', '3.11', '3.12']
16    steps:
17      - uses: actions/checkout@v4
18
19      - uses: actions/setup-python@v5
20        with:
21          python-version: ${{ matrix.python-version }}
22
23      - name: Install Rust
24        uses: dtolnay/rust-toolchain@stable
25
26      - name: Install maturin
27        run: pip install maturin pytest
28
29      - name: Build and install
30        run: maturin develop
31
32      - name: Run tests
33        run: pytest tests/

【進階】效能優化

編譯優化

1# Cargo.toml
2[profile.release]
3lto = "fat"         # 最佳化連結(較慢編譯)
4codegen-units = 1   # 單一編譯單元(更好優化)
5panic = "abort"     # 不展開 panic(較小二進位)
6strip = true        # 移除符號
7
8# 針對特定 CPU 優化
9# RUSTFLAGS="-C target-cpu=native" maturin build --release

減小二進位大小

 1# Cargo.toml
 2[profile.release]
 3opt-level = "z"     # 優化大小而非速度
 4lto = true
 5strip = true
 6panic = "abort"
 7
 8[dependencies]
 9# 使用 features 減少不需要的程式碼
10pyo3 = { version = "0.23", default-features = false, features = ["extension-module"] }

除錯技巧

1# 保留除錯符號
2maturin develop --profile dev
3
4# 啟用 Rust backtrace
5RUST_BACKTRACE=1 python -c "import my_module; my_module.buggy_function()"
6
7# 使用 lldb/gdb
8lldb python
9(lldb) run -c "import my_module; my_module.buggy_function()"

【常見問題】疑難排解

編譯錯誤

 1問題:找不到 Python
 2
 3解決:
 41. 確認 Python 在 PATH 中
 52. 使用 --interpreter 指定路徑
 6   maturin develop --interpreter /usr/bin/python3.11
 7
 8問題:連結錯誤(Linux)
 9
10解決:
111. 安裝 python3-dev
12   sudo apt install python3-dev
132. 安裝 build-essential
14   sudo apt install build-essential
15
16問題:找不到 PyO3
17
18解決:
191. 確認 Cargo.toml 中有正確的 pyo3 依賴
202. 執行 cargo update

執行時錯誤

 1問題:ImportError: undefined symbol
 2
 3原因:模組和 Python 版本不匹配
 4
 5解決:
 61. 重新建構
 7   maturin develop
 82. 確認使用正確的 Python
 9   which python
10
11問題:記憶體錯誤 / Segfault
12
13原因:通常是 unsafe 程式碼或 GIL 問題
14
15解決:
161. 檢查 unsafe 區塊
172. 確認正確使用 py.allow_threads()
183. 使用 RUST_BACKTRACE=1 獲取堆疊追蹤

完整範例專案

專案結構

 1fibonacci_rs/
 2├── Cargo.toml
 3├── pyproject.toml
 4├── src/
 5│   └── lib.rs
 6├── python/
 7│   └── fibonacci_rs/
 8│       ├── __init__.py
 9│       └── py.typed
10├── tests/
11│   └── test_fib.py
12└── README.md

完整程式碼

 1# Cargo.toml
 2[package]
 3name = "fibonacci_rs"
 4version = "0.1.0"
 5edition = "2021"
 6
 7[lib]
 8name = "fibonacci_rs"
 9crate-type = ["cdylib"]
10
11[dependencies]
12pyo3 = { version = "0.23", features = ["extension-module"] }
13
14[profile.release]
15lto = true
16codegen-units = 1
 1# pyproject.toml
 2[build-system]
 3requires = ["maturin>=1.5,<2.0"]
 4build-backend = "maturin"
 5
 6[project]
 7name = "fibonacci-rs"
 8version = "0.1.0"
 9description = "Fast Fibonacci implementation in Rust"
10requires-python = ">=3.8"
11
12[tool.maturin]
13python-source = "python"
14module-name = "fibonacci_rs._core"
 1// src/lib.rs
 2use pyo3::prelude::*;
 3
 4/// 計算 Fibonacci 數列第 n 項
 5#[pyfunction]
 6fn fib(n: u64) -> u64 {
 7    if n <= 1 {
 8        return n;
 9    }
10    let mut a = 0u64;
11    let mut b = 1u64;
12    for _ in 2..=n {
13        let tmp = a + b;
14        a = b;
15        b = tmp;
16    }
17    b
18}
19
20/// 計算 Fibonacci 數列前 n 項
21#[pyfunction]
22fn fib_sequence(py: Python<'_>, n: usize) -> Vec<u64> {
23    py.allow_threads(|| {
24        let mut seq = Vec::with_capacity(n);
25        let (mut a, mut b) = (0u64, 1u64);
26        for _ in 0..n {
27            seq.push(a);
28            let tmp = a + b;
29            a = b;
30            b = tmp;
31        }
32        seq
33    })
34}
35
36#[pymodule]
37fn _core(m: &Bound<'_, PyModule>) -> PyResult<()> {
38    m.add_function(wrap_pyfunction!(fib, m)?)?;
39    m.add_function(wrap_pyfunction!(fib_sequence, m)?)?;
40    Ok(())
41}
1# python/fibonacci_rs/__init__.py
2from ._core import fib, fib_sequence
3
4__all__ = ["fib", "fib_sequence"]
5__version__ = "0.1.0"
 1# tests/test_fib.py
 2import fibonacci_rs
 3
 4def test_fib_zero():
 5    assert fibonacci_rs.fib(0) == 0
 6
 7def test_fib_one():
 8    assert fibonacci_rs.fib(1) == 1
 9
10def test_fib_ten():
11    assert fibonacci_rs.fib(10) == 55
12
13def test_fib_sequence():
14    seq = fibonacci_rs.fib_sequence(10)
15    assert seq == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

思考題

  1. 為什麼 Maturin 使用 cdylib crate type?與 rlib 有什麼差異?
  2. abi3 功能如何減少需要建構的 wheel 數量?有什麼限制?
  3. 在 CI 中建構跨平台 wheel 時,最大的挑戰是什麼?

實作練習

  1. 建立一個新的 Maturin 專案,實現一個簡單的字串處理函式
  2. 設定 GitHub Actions 自動建構並發布到 TestPyPI
  3. 比較 maturin developmaturin develop --release 的編譯時間和執行效能

延伸閱讀


上一章:PyO3 基礎 下一章:實戰案例分析