Maturin Cheat Sheet
Overview
Maturin is a build tool for building and publishing Python packages with Rust extensions (via PyO3, rust-cpython, or cffi bindings). It compiles Rust code into native Python extension modules and packages them as wheels that can be installed with pip. Maturin handles cross-compilation, manylinux compliance, and multi-platform builds.
Maturin integrates with the Python packaging ecosystem as a PEP 517 build backend, meaning it works seamlessly with pip, build, and other standard tools. It supports building for multiple Python versions, architectures, and operating systems from a single command.
Installation
# Via pip
pip install maturin
# Via pipx
pipx install maturin
# Via cargo
cargo install maturin
# macOS
brew install maturin
# Verify
maturin --version
Core Commands
| Command | Description |
|---|---|
maturin init | Initialize a new project |
maturin new <name> | Create new project from template |
maturin develop | Build and install locally for development |
maturin build | Build wheel packages |
maturin publish | Build and publish to PyPI |
maturin sdist | Build source distribution |
maturin upload | Upload pre-built wheels |
maturin list-python | List detected Python installations |
maturin generate-ci | Generate CI configuration |
Project Setup
Create New Project
# Create with PyO3 bindings (default)
maturin new my-rust-lib
cd my-rust-lib
# Create with cffi bindings
maturin new --bindings cffi my-cffi-lib
# Initialize in existing directory
maturin init --bindings pyo3
Project Structure
my-rust-lib/
Cargo.toml
pyproject.toml
src/
lib.rs
python/
my_rust_lib/
__init__.py
Cargo.toml
[package]
name = "my-rust-lib"
version = "0.1.0"
edition = "2021"
[lib]
name = "my_rust_lib"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.22", features = ["extension-module"] }
pyproject.toml
[build-system]
requires = ["maturin>=1.0,<2.0"]
build-backend = "maturin"
[project]
name = "my-rust-lib"
version = "0.1.0"
description = "A Python library written in Rust"
requires-python = ">=3.9"
[tool.maturin]
python-source = "python"
features = ["pyo3/extension-module"]
module-name = "my_rust_lib._core"
Rust Code (PyO3)
// src/lib.rs
use pyo3::prelude::*;
#[pyfunction]
fn sum_as_string(a: usize, b: usize) -> PyResult<String> {
Ok((a + b).to_string())
}
#[pyfunction]
fn fibonacci(n: u64) -> PyResult<u64> {
let mut a: u64 = 0;
let mut b: u64 = 1;
for _ in 0..n {
let temp = b;
b = a + b;
a = temp;
}
Ok(a)
}
#[pymodule]
fn my_rust_lib(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
m.add_function(wrap_pyfunction!(fibonacci, m)?)?;
Ok(())
}
Development Workflow
# Build and install in current virtualenv (editable-like)
maturin develop
# With release optimizations
maturin develop --release
# With specific Python
maturin develop --interpreter python3.12
# With extra features
maturin develop --features "feature1,feature2"
# Build wheel for distribution
maturin build --release
# Build for specific Python versions
maturin build --release --interpreter python3.10 python3.11 python3.12
Configuration
Cross-Compilation
# Build for Linux (manylinux)
maturin build --release --target x86_64-unknown-linux-gnu
# Build for macOS universal
maturin build --release --target universal2-apple-darwin
# Build for Windows
maturin build --release --target x86_64-pc-windows-msvc
# Build manylinux wheels (using Docker)
maturin build --release --manylinux 2_28
Advanced pyproject.toml
[tool.maturin]
# Python source directory
python-source = "python"
# Rust features to enable
features = ["pyo3/extension-module"]
# Strip debug symbols
strip = true
# Module name (if different from package)
module-name = "my_package._native"
# Manylinux compatibility
manylinux = "2_28"
# Include additional data files
include = [
{ path = "LICENSE", format = "sdist" },
{ path = "py.typed", format = "wheel" },
]
# Exclude files
exclude = ["tests/", "benches/"]
Advanced Usage
Publishing
# Publish to PyPI
maturin publish --username __token__ --password pypi-xxx
# Publish to Test PyPI
maturin publish --repository-url https://test.pypi.org/legacy/
# Build and upload separately
maturin build --release
maturin upload target/wheels/*.whl
CI/CD Generation
# Generate GitHub Actions workflow
maturin generate-ci github > .github/workflows/ci.yml
# Generate for multiple platforms
maturin generate-ci --platform linux --platform macos --platform windows github
Python Classes from Rust
use pyo3::prelude::*;
#[pyclass]
struct Calculator {
value: f64,
}
#[pymethods]
impl Calculator {
#[new]
fn new() -> Self {
Calculator { value: 0.0 }
}
fn add(&mut self, n: f64) {
self.value += n;
}
fn result(&self) -> f64 {
self.value
}
fn __repr__(&self) -> String {
format!("Calculator(value={})", self.value)
}
}
#[pymodule]
fn my_rust_lib(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Calculator>()?;
Ok(())
}
Async Support
use pyo3::prelude::*;
use pyo3_asyncio_0_21::tokio::future_into_py;
#[pyfunction]
fn fetch_url<'py>(py: Python<'py>, url: String) -> PyResult<Bound<'py, PyAny>> {
future_into_py(py, async move {
let response = reqwest::get(&url).await.map_err(|e| {
pyo3::exceptions::PyIOError::new_err(e.to_string())
})?;
let text = response.text().await.map_err(|e| {
pyo3::exceptions::PyIOError::new_err(e.to_string())
})?;
Ok(text)
})
}
Troubleshooting
| Issue | Solution |
|---|---|
maturin develop fails | Activate virtualenv first; check Rust toolchain |
| Import error after build | Verify module-name in pyproject.toml matches |
| Manylinux build fails | Use --manylinux off for testing; check glibc version |
| Cross-compile errors | Install target: rustup target add <target> |
| PyO3 version mismatch | Match PyO3 version with maturin compatibility |
| Slow builds | Use maturin develop (debug) for iteration |
# List available Python interpreters
maturin list-python
# Build with verbose output
maturin build -v
# Check what would be built
maturin build --release --out dist/
ls dist/
# Debug: show resolved configuration
maturin develop -v 2>&1 | head -20