🦀 High-performance Rust functions + 🐍 Python productivity = Perfect integration
This template demonstrates how to integrate Rust with Python using PyO3, providing a solid foundation for projects that need the performance of Rust with the convenience of Python. It includes 6 mathematical functions showcasing different aspects of Rust-Python integration: from simple calculations to complex matrix operations with comprehensive error handling.
Perfect for: Performance-critical applications, numerical computing, learning Rust-Python integration, and building high-performance Python extensions.
What's included: 95 tests (32 Rust + 63 Python), enterprise-grade code quality with type stubs, complete development workflow, and comprehensive documentation.
rust-with-python/
├── digits-calculator/ # Rust crate (PyO3 extension)
│ ├── src/
│ │ ├── lib.rs # PyO3 wrapper functions
│ │ └── math.rs # Pure Rust mathematical functions
│ ├── Cargo.toml # Rust dependencies & metadata
│ └── pyproject.toml # Python build configuration
├── tests/
│ └── test_digits_calculator.py # 63 comprehensive Python tests
├── digits_calculator.pyi # Type stubs for PyO3 module
├── main.py # Example usage demonstration
├── Makefile # 15 development commands
├── pyproject.toml # Project configuration & dependencies
├── README.md # This documentation
├── CONTRIBUTING.md # Development guidelines
├── TEMPLATE_COMPLETION.md # Template transformation report
├── MACOS_TROUBLESHOOTING.md # Comprehensive macOS guide
└── uv.lock # Dependency lock file
Two-module design for maximum testability:
src/math.rs: Pure Rust mathematical functions (32 unit tests, no PyO3 dependencies)src/lib.rs: PyO3 wrapper layer that exposes functions to Python (handles type conversion and error mapping)digits_calculator.pyi: Type stubs for perfect IDE support and static analysis
This separation allows:
- Fast Rust unit testing without Python FFI overhead
- Clean separation of business logic and binding code
- Complete type safety with modern Python type checking
- Zero warnings from basedpyright/pylsp/mypy
- Professional code organization
- Python 3.8+ (tested with 3.13)
- Rust 1.70+ (latest stable recommended)
- uv for Python dependency management (install guide)
# Clone the repository
git clone https://github.com/macurandb/rust-with-python.git
cd rust-with-python
# Install and build everything
make install
# Verify installation
make testThis will:
- Create a Python virtual environment with
uv - Install all Python dependencies (including mypy for type checking)
- Build the Rust extension module
- Run all 95 tests to verify everything works
# Run the demonstration
make runExample output:
======================================================================
Welcome to rust-with-python!
Demonstrating Python and Rust Integration with PyO3
======================================================================
📊 Testing calculate_pi function (Rust Implementation):
----------------------------------------------------------------------
π approximation (1,000,000 iterations using Leibniz formula):
Result: 3.1415916535897743
Expected: 3.141592653589793
Error: 0.0000010000
🔢 Testing sum_as_string function (Rust Implementation):
----------------------------------------------------------------------
Sum of 10 + 20 = 30
======================================================================
✅ All demonstrations completed successfully!
======================================================================
Shows all available commands with descriptions.
Complete setup: creates virtual environment, installs dependencies, builds Rust module.
Builds the Rust extension module only.
Quick verification without full rebuild.
Runs the main demonstration script.
Runs all tests (32 Rust unit tests + 63 Python integration tests = 95 total tests).
Runs only the Rust unit tests (32 tests, fast, no Python FFI).
Runs only the Python integration tests with pytest (63 tests with full type checking).
Checks code quality for both Rust (clippy) and Python (ruff) with zero-tolerance policy.
Example output:
🔍 Checking Rust code with clippy...
✅ Rust code quality checks passed!
🔍 Checking Python code with ruff...
✅ Python code quality checks passed!Quality standards: Zero warnings policy, modern type annotations, enterprise-grade linting.
Rust-only linting with clippy.
Python-only linting with ruff.
Formats all code (Rust with cargo fmt, Python with ruff format).
Rust-only formatting.
Python-only formatting.
Removes all build artifacts and virtual environments.
Complete workflow: install, build, test, lint, and run.
importdigits_calculator# High-precision π calculationpi=digits_calculator.calculate_pi(1_000_000)
print(f"π ≈ {pi}")
# Matrix multiplicationa= [[1.0, 2.0], [3.0, 4.0]]
b= [[5.0, 6.0], [7.0, 8.0]]
result=digits_calculator.matrix_multiply(a, b)
print(f"Result: {result}") # [[19.0, 22.0], [43.0, 50.0]]# Safe mathematical operationssqrt_result=digits_calculator.safe_sqrt(25.0) # 5.0division_result=digits_calculator.divide(10.0, 3.0) # 3.333...factorial_result=digits_calculator.factorial(5) # 120All functions are implemented in pure Rust for maximum performance:
Calculates π using the Leibniz formula: π/4 = 1 - 1/3 + 1/5 - 1/7 + ...
# More iterations = higher precisionpi_low=digits_calculator.calculate_pi(100) # ~3.131pi_medium=digits_calculator.calculate_pi(10000) # ~3.1414pi_high=digits_calculator.calculate_pi(1000000) # ~3.141592importmatherror=abs(pi_high-math.pi) # Very small errorPerformance: 1,000,000 iterations in ~0.004s
High-performance matrix multiplication with comprehensive validation.
# 2D transformation examplerotation= [[0.866, -0.5], [0.5, 0.866]] # 30° rotationpoint= [[1.0], [0.0]]
rotated=digits_calculator.matrix_multiply(rotation, point)
# Result: [[0.866], [0.5]]# Supports rectangular matricesa= [[1, 2], [3, 4], [5, 6]] # 3x2b= [[7, 8, 9], [10, 11, 12]] # 2x3result=digits_calculator.matrix_multiply(a, b) # 3x3 resultFeatures: Dimension validation, empty matrix detection, floating-point precision handling.
Simple integer addition returning string result (useful for very large numbers).
result=digits_calculator.sum_as_string(999999999, 1)
print(result) # "1000000000" (as string)All functions include proper error handling with specific Python exception types:
Safe division with zero-division protection.
try:
result=digits_calculator.divide(10.0, 2.0) # 5.0print(f"Result: {result}")
exceptZeroDivisionErrorase:
print(f"Division error: {e}")
# This will raise ZeroDivisionErrortry:
digits_calculator.divide(10.0, 0.0)
exceptZeroDivisionErrorase:
print("Cannot divide by zero!") # Exception caughtSquare root calculation with negative number protection.
try:
result=digits_calculator.safe_sqrt(16.0) # 4.0print(f"√16 = {result}")
exceptValueErrorase:
print(f"Square root error: {e}")
# This will raise ValueErrortry:
digits_calculator.safe_sqrt(-9.0)
exceptValueErrorase:
print("Cannot calculate square root of negative number!")Factorial calculation with input validation and overflow detection.
try:
result=digits_calculator.factorial(5) # 120print(f"5! = {result}")
exceptValueErrorase:
print(f"Input error: {e}")
exceptOverflowErrorase:
print(f"Result too large: {e}")
# This will raise ValueErrortry:
digits_calculator.factorial(-5)
exceptValueErrorase:
print("Factorial not defined for negative numbers!")make testExpected output:
🦀 Running Rust unit tests (pure math module)...
running 30 tests
test result: ok. 30 passed; 0 failed
🐍 Running Python integration tests...
collected 63 items
================================= 63 passed in 0.03s =================================
✅ All tests passed!
make test-rust # Fast Rust unit tests only
make test-python # Python integration tests onlyThe Python tests use pytest with professional patterns:
- Parametrized tests for comprehensive coverage
- Fixtures for test data management
- Exception testing with
pytest.raises() - Type verification and consistency checks
- Performance benchmarks included
Example test:
@pytest.mark.parametrize("iterations,max_error", [ (1_000, 0.01), (10_000, 0.001), (1_000_000, 0.001),])deftest_calculate_pi_accuracy(self, iterations, max_error):
result=digits_calculator.calculate_pi(iterations)
error=abs(result-math.pi)
asserterror<max_errorTotal: 95 tests (100% passing)
Pure Rust function testing without PyO3 dependencies:
- calculate_pi tests (6 tests): Accuracy, edge cases, convergence, high-precision algorithms
- matrix_multiply tests (10 tests): Basic operations, dimensions, error cases, floating-point precision
- divide tests (4 tests): Valid operations, error handling, negative numbers
- safe_sqrt tests (3 tests): Valid inputs, negative handling, edge cases
- factorial tests (5 tests): Valid calculations, error cases, large numbers
- sum_as_string tests (4 tests): Basic operations, edge cases, large numbers
# Run with detailscd digits-calculator && cargo test -- --nocapture
test math::tests::test_calculate_pi_zero_iterations ... ok
test math::tests::test_calculate_pi_high_precision ... ok
test math::tests::test_matrix_multiply_basic_2x2 ... ok
test math::tests::test_divide_basic ... ok
test math::tests::test_safe_sqrt_basic ... ok
test math::tests::test_factorial_basic ... ok
test math::tests::test_sum_as_string_basic ... ok
# ... and 25 more testsComprehensive PyO3 integration testing:
- TestCalculatePi: 10 parametrized tests (ranges, accuracy, consistency)
- TestMatrixMultiply: 11 tests (valid operations, error cases, edge cases)
- TestSumAsString: 8 parametrized tests (operations, types, consistency)
- TestModuleIntegration: 10 parametrized tests (exports, callability)
- TestDivide: 7 tests (operations, exception handling)
- TestSafeSqrt: 8 tests (valid inputs, exception handling)
- TestFactorial: 9 tests (calculations, exception handling)
# Example parametrized test@pytest.mark.parametrize("a,b,expected", [ (10.0, 2.0, 5.0), (7.0, 2.0, 3.5), (-10.0, 2.0, -5.0),])deftest_divide_valid_operations(self, a, b, expected):
result=digits_calculator.divide(a, b)
assertabs(result-expected) <1e-10================================= test session starts =================================
collected 63 items
tests/test_digits_calculator.py::TestCalculatePi::test_calculate_pi_ranges[0-expected_range0] PASSED
tests/test_digits_calculator.py::TestMatrixMultiply::test_matrix_multiply_valid[a0-b0-expected0] PASSED
tests/test_digits_calculator.py::TestDivide::test_divide_valid_operations[10.0-2.0-5.0] PASSED
tests/test_digits_calculator.py::TestSafeSqrt::test_safe_sqrt_valid_inputs[16.0-4.0] PASSED
tests/test_digits_calculator.py::TestFactorial::test_factorial_valid_inputs[5-120] PASSED
# ... 58 more tests all PASSED
================================= 63 passed in 0.03s =================================
Type Safety: All tests include comprehensive type hints and pass strict mypy checking.
# Test specific functioncd digits-calculator && cargo test calculate_pi
uv run pytest tests/ -k "calculate_pi" -v
# Test specific class
uv run pytest tests/test_digits_calculator.py::TestMatrixMultiply -v
# Test with coverage
uv run pytest tests/ --tb=shortThe project maintains zero-warning policy with modern tooling:
- Type stubs:
digits_calculator.pyiprovides complete PyO3 module types - Modern annotations: Python 3.13+ style (
listinstead ofList) - Strict mypy: Full type checking with enterprise-grade configuration
- Zero basedpyright errors: Perfect IDE support and static analysis
make lint-rust
# Runs: cargo clippy -- -D warningsClippy checks for:
- Performance improvements
- Memory safety issues
- Idiomatic Rust patterns
- Potential bugs
make lint-python
# Runs: ruff check .Ruff checks for:
- PEP 8 style violations
- Import organization
- Modern type annotations (UP006, UP035)
- Code complexity and best practices
make lint
# Runs both Rust and Python linting# Zero tolerance policy: all warnings treated as errorsLocated in pyproject.toml:
[tool.ruff.lint]
line-length = 100target-version = "py313"extend-select = ["E", "F", "W", "I", "UP", "C4"][tool.mypy]
python_version = "3.13"strict = truewarn_return_any = truedisallow_untyped_defs = truecheck_untyped_defs = truedigits_calculator.pyi provides complete type information for the PyO3 module:
- Resolves basedpyright/pylsp attribute errors
- Enables perfect IDE autocompletion
- Supports static type analysis
Standard clippy with deny-warnings policy in Makefile.
Add your function to digits-calculator/src/math.rs:
pubfnfactorial(n:i32) -> Result<u64,String>{if n < 0{returnErr("Factorial is not defined for negative numbers".to_string());}letmut result:u64 = 1;for i in2..=(n asu64){
result *= i;}Ok(result)}Add tests in the same file:
#[cfg(test)]mod tests {usesuper::*;#[test]fntest_factorial_basic(){assert_eq!(factorial(5).unwrap(),120);assert_eq!(factorial(0).unwrap(),1);}}Add PyO3 wrapper in digits-calculator/src/lib.rs:
#[pyfunction]fnfactorial(n:i32) -> PyResult<u64>{match math::factorial(n){Ok(result) => Ok(result),Err(msg) => Err(PyErr::new::<pyo3::exceptions::PyValueError,_>(msg)),}}#[pymodule]fndigits_calculator(m:&Bound<'_,PyModule>) -> PyResult<()>{
m.add_wrapped(wrap_pyfunction!(factorial))?;Ok(())}Create Python tests in tests/test_digits_calculator.py:
deftest_factorial_basic():
result=digits_calculator.factorial(5)
assertresult==120make clean && make install
make testgit clone <your-fork>cd rust-with-python
make install- Edit Rust code in
digits-calculator/src/ - Add tests as you develop
- Update documentation
make fmt # Format all code
make lint # Check code qualityFix any issues before continuing.
make clean # Clean previous builds
make install # Rebuild everything
make test# Run all 93 testsAll tests must pass:
✅ Rust unit tests passed! (30/30)
✅ Python integration tests passed! (63/63)
make runVerify your changes work in the demo.
# Complete workflow
make all
# This runs:# 1. make clean# 2. make install # 3. make test# 4. make lint# 5. make runExpected output:
🧹 Cleaning up build artifacts...
📦 Installing dependencies and building...
🧪 Running all tests...
✅ All tests passed! (95/95)
🔍 Checking code quality...
✅ All quality checks passed! (Zero warnings)
🚀 Running demonstration...
✅ Application completed successfully!
make clean
make installThis rebuilds everything from scratch.
Ensure you're in the virtual environment:
source .venv/bin/activate
python -c "import digits_calculator; print('✅ Import successful')"Run tests individually to isolate issues:
make test-rust # Check Rust unit tests
make test-python # Check Python integration testsCheck the specific error messages for guidance.
This template includes specific solutions for common PyO3 issues on macOS:
If maturin fails with linking errors when Conda is active:
unset CONDA_PREFIX
make buildOur Makefile automatically handles this for you.
If you encounter linking errors, ensure Xcode command line tools are installed:
xcode-select --installFor M1/M2/M3 Macs, if you see architecture warnings:
export ARCHFLAGS="-arch arm64"
make buildUse uv for consistent Python management (already configured):
make clean && make installFor comprehensive macOS troubleshooting, see MACOS_TROUBLESHOOTING.md
- PyO3 Documentation
- maturin User Guide
- Rust Book
- uv Documentation
- macOS Troubleshooting Guide - Comprehensive macOS-specific solutions
- Rust 1.70+: Core mathematical functions
- Python 3.13+: Interface and testing with modern type annotations
- PyO3 0.27.0: Rust-Python bindings
- maturin: Build tool for PyO3 projects
- pytest: Python testing framework
- uv: Fast Python package manager
- ruff: Python linting and formatting (zero warnings)
- mypy: Static type checking (strict mode)
- cargo clippy: Rust linting (deny warnings)
This project is licensed under the MIT License. See LICENSE for details.
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
Quick contribution checklist:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass (
make test) - Check code quality (
make lint) - Submit a pull request
- Rename the module: Change
digits-calculatorto your project name - Replace functions: Modify
src/math.rswith your own functions - Update tests: Add comprehensive tests for your functions
- Documentation: Update this README with your project details
- Dependencies: Add any additional Rust or Python dependencies you need
New to Rust-Python integration? Follow this path:
- Start here: Run
make testand explore the existing functions - Understand the architecture: Read
src/math.rs(pure Rust) andsrc/lib.rs(PyO3 bindings) - Explore type safety: Check
digits_calculator.pyifor complete type definitions - Modify existing functions: Try changing the
calculate_piimplementation - Add a simple function: Start with a basic math function
- Advanced features: Explore error handling and complex data types like matrices
- Type annotations: Learn modern Python type hints and mypy integration
- Performance: Benchmark your Rust vs pure Python implementations
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Template Questions: Open an issue with the
templatelabel
🎉 Happy coding! Enjoy the power of Rust with the convenience of Python.