AutoZig enables safe, ergonomic interop between Rust and Zig code, inspired by autocxx for C++.
Quick Start โข Tutorial โข Features โข Documentation โข Examples โข Contributing
|
Encapsulated Unsafe - FFI complexity is isolated; business logic remains Compile-time code generation - Zig code is compiled during |
Automatic type conversion - Safe bindings between Rust and Zig types Write Zig inline - Embed Zig code directly in your Rust files |
# Cargo.toml
[dependencies]
autozig = "0.1"
[build-dependencies]
autozig-build = "0.1"// build.rs
fn main() -> anyhow::Result<()> {
autozig_build::build("src")?;
Ok(())
}// src/main.rs
use autozig::autozig;
autozig! {
// Zig implementation
const std = @import("std");
export fn compute_hash(ptr: [*]const u8, len: usize) u64 {
const data = ptr[0..len];
var hash: u64 = 0;
for (data) |byte| {
hash +%= byte;
}
return hash;
}
---
// Rust signatures (optional - enables safe wrappers)
fn compute_hash(data: &[u8]) -> u64;
}
fn main() {
let data = b"Hello AutoZig";
let hash = compute_hash(data); // Safe call, no unsafe!
println!("Hash: {}", hash);
}Latest Release - AutoZig now supports WebAssembly with Zig + Rust static linking for extreme performance in browsers!
Compile Zig and Rust into a single WASM file with zero-copy memory sharing:
use wasm_bindgen::prelude::*;
use autozig::autozig;
autozig! {
// Zig code compiled to WASM
export fn invert_colors(ptr: [*]u8, len: usize) void {
var i: usize = 0;
while (i < len) : (i += 4) {
ptr[i] = 255 - ptr[i]; // R
ptr[i+1] = 255 - ptr[i+1]; // G
ptr[i+2] = 255 - ptr[i+2]; // B
}
}
---
fn invert_colors(data: &mut [u8]);
}
#[wasm_bindgen]
pub fn apply_filter(mut data: Vec<u8>) -> Vec<u8> {
invert_colors(&mut data); // Zero-copy call to Zig
data
}Features:
- โ
Static Linking: Zig + Rust โ Single
.wasmfile - โ Zero-Copy: Shared linear memory, no data copying
- โ
SIMD Optimization: Zig
@Vector+ WASM SIMD128 instructions - โ High Performance: 3-5x faster than pure JavaScript, 3x faster than Rust native
- โ
Small Binary: Optimized with
-O ReleaseFast+wasm-opt
Real-World Performance (Image Filter Benchmark - 2.1 MB image):
| Implementation | Processing Time | Throughput | Relative Performance |
|---|---|---|---|
| โก AutoZig (Zig SIMD) | 0.80 ms | 2631.84 MB/s | Baseline (1.00x) |
| ๐ฆ Rust Native | 2.50 ms | 842.19 MB/s | 3.13x slower |
| ๐จ JavaScript | 3.80 ms | 554.07 MB/s | 4.75x slower |
Why AutoZig is faster:
- ๐ฅ SIMD128 Instructions: Zig's
@Vector(16, u8)compiles tov128.load/sub/store - ๐ Zero Abstractions: Direct memory manipulation with no runtime overhead
- โก Compiler Optimization: Zig + LLVM's aggressive optimizations
- ๐ฏ Saturating Arithmetic: Hardware-accelerated
+|and-|operations
Build for WASM:
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
wasm-pack build --target web๐ Learn More: examples/wasm_filter | docs/PHASE_5_WASM_DESIGN.md
AutoZig is ready for the future of WebAssembly with full Memory64 support:
- โ >4GB Memory Support: Seamlessly address huge heap spaces
- โ 64-bit Pointers: Native 64-bit arithmetic in both Rust and Zig
- โ
Verified Demo:
examples/wasm64bitdemonstrates working Memory64 interop
// Zig can natively access >4GB memory space
export fn process_huge_array(ptr: [*]u8, len: u64) void {
// ...
}Safe FFI - AutoZig now implements an Explicit Lifetime Bridging Protocol to prevent leaks and UAF across the Rust-Zig boundary.
AutoZig enforces a strict "Owner Frees" policy using the standardized ZigBuffer and ZigBox smart pointers:
- Zig -> Rust: Zig allocates, Rust takes ownership via
ZigBox<T>. WhenZigBoxdrops, it calls back into Zig to free memory. - Rust -> Zig: Rust allocates, wraps in
ZigBufferwith a destructor callback. Zig calls this callback when done.
// Safe Import (Zig -> Rust)
autozig! {
const std = @import("std");
// Zig allocates and returns buffer with free_fn attached
export fn allocate_data(size: usize) ZigBuffer { ... }
---
#### ๐ก๏ธ Safe Bridge Pattern & Library Support
To achieve **Zero Unsafe** in your business logic, AutoZig provides built-in safety tools:
- `ZigBox::new(raw)`: Safely wraps FFI buffers (trusting the protocol).
- `ZigBuffer::from(Vec<T>)`: Automatically handles ownership transfer and cleanup.
- `autozig::rust_free_vec`: Standardized destructor for Rust vectors.
```rust
// 1. Define the Bridge (src/ffi.rs)
// This module contains the macro and raw FFI bindings
mod zig_bridge {
use autozig::prelude::*;
use autozig::ffi_types::{ZigBuffer, ZigBox};
// Nest the raw bindings to avoid name collisions
mod raw {
use super::*;
autozig! {
const std = @import("std");
// Zig allocates and returns buffer with free_fn attached
export fn allocate_data(size: usize) ZigBuffer { ... }
---
// Raw FFI Signature
fn allocate_data(size: usize) -> ZigBuffer;
}
}
// Safe Public API
// ๐ก๏ธ All UNSAFE code is contained/wrapped here!
pub fn get_data(size: usize) -> ZigBox<u8> {
// Safe: ZigBox::new wraps the raw buffer
ZigBox::new(unsafe { raw::allocate_data(size) })
}
}
// 2. User Business Logic (0 Unsafe!)
fn main() {
// 100% Safe Rust Code
let data = zig_bridge::get_data(1024);
// Auto-free when dropped -> No leaks, no unsafe blocks
println!("Got {} bytes", data.as_slice().len());
}Note
Safety Design: By moving the unsafe FFI definitions into a nested raw module and exposing safe wrappers, you ensure that your main application logic is mathematically proven to be free of unsafe block usage, relying on the ZigBox invariants.
We verified this protocol with a rigorous 1,000,000 iteration stress test:
- Zig Alloc -> Rust Drop: 0 bytes leaked
- Rust Alloc -> Zig Drop: 0 bytes leaked
- Status: โ 0 Leaks Detected
๐ Learn More: examples/leak_test
Latest Release - AutoZig Phase 1-4 fully complete! New Stream support, zero-copy optimization, SIMD detection and more advanced features!
Async data stream support based on the futures::Stream trait:
use autozig::stream::create_stream;
use futures::StreamExt;
let (tx, stream) = create_stream::<U32Value>();
futures::pin_mut!(stream);
while let Some(result) = stream.next().await {
println!("Received: {:?}", result);
}Features:
- โ
futures::Streamtrait implementation - โ Async data stream processing
- โ Error handling and state management
- โ Seamless integration with Zig generators
Zero-copy buffer passing for efficient Zig โ Rust data transfer with no overhead:
use autozig::zero_copy::ZeroCopyBuffer;
// Zig generates data, Rust receives with zero-copy
let buffer = ZeroCopyBuffer::from_zig_vec(raw_vec);
let data = buffer.into_vec(); // Zero-copy conversionPerformance:
- โ 1.93x speedup (compared to copying)
- โ Zero additional memory allocation
- โ Completely safe API
Compile-time SIMD feature detection and automatic optimization:
// build.rs
let simd_config = autozig_build::detect_and_report();
println!("Detected SIMD: {}", simd_config.description);Supported Features:
- โ x86_64: SSE2, SSE4.2, AVX, AVX2, AVX-512
- โ ARM: NEON
- โ Zig automatic vectorization optimization
๐ Learn More: examples/stream_basic | examples/zero_copy | examples/simd_detect
AutoZig supports generic monomorphization and async FFI!
Write generic Rust functions and let AutoZig generate type-specific Zig implementations:
use autozig::autozig;
autozig! {
// Zig implementations for each type
export fn sum_i32(data_ptr: [*]const i32, data_len: usize) i32 {
var total: i32 = 0;
var i: usize = 0;
while (i < data_len) : (i += 1) {
total += data_ptr[i];
}
return total;
}
export fn sum_f64(data_ptr: [*]const f64, data_len: usize) f64 {
var total: f64 = 0.0;
var i: usize = 0;
while (i < data_len) : (i += 1) {
total += data_ptr[i];
}
return total;
}
---
// Declare once, use with multiple types!
#[monomorphize(i32, f64, u64)]
fn sum<T>(data: &[T]) -> T;
}
fn main() {
let ints = vec![1i32, 2, 3, 4, 5];
let floats = vec![1.5f64, 2.5, 3.5];
println!("Sum of ints: {}", sum_i32(&ints)); // 15
println!("Sum of floats: {}", sum_f64(&floats)); // 7.5
}Features:
- โ C++-style template instantiation for Rust generics
- โ
Automatic name mangling (
process<T>โprocess_i32,process_f64) - โ
Type substitution engine (handles
&[T],&mut [T], nested types) - โ Zero runtime overhead
Write async Rust APIs backed by synchronous Zig implementations:
use autozig::include_zig;
include_zig!("src/compute.zig", {
// Declare async functions
async fn heavy_computation(data: i32) -> i32;
async fn process_data(input: &[u8]) -> usize;
});
#[tokio::main]
async fn main() {
// Async API - automatically uses tokio::spawn_blocking
// Note: Borrowed arguments (like &[u8]) are copied into the task
// to ensure 'static lifetime required by spawn_blocking.
// For zero-copy async, use owned types like Vec<u8> or ZigBox.
let result = heavy_computation(42).await;
println!("Result: {}", result);
// Concurrent execution
let tasks = vec![
tokio::spawn(async { heavy_computation(10).await }),
tokio::spawn(async { heavy_computation(20).await }),
tokio::spawn(async { heavy_computation(30).await }),
];
let results = futures::future::join_all(tasks).await;
println!("Concurrent results: {:?}", results);
}Zig side (stays synchronous!):
// src/compute.zig
export fn heavy_computation(data: i32) i32 {
// Write normal synchronous Zig code
// No async/await needed!
return data * 2;
}Features:
- โ
Rust: Async wrappers using
tokio::spawn_blocking - โ Zig: Synchronous implementations (no async/await complexity)
- โ Thread pool offload prevents blocking async runtime
- โ Automatic parameter capture and conversion
๐ Learn More: examples/generics | examples/async
๐ Run Zig unit tests as part of your Rust test suite!
AutoZig integrates Zig unit tests into the Rust test framework!
// build.rs
fn main() -> anyhow::Result<()> {
autozig_build::build("src")?;
autozig_build::build_tests("zig")?; // Compile Zig tests
Ok(())
}// zig/math.zig
export fn factorial(n: u32) u64 {
// ... implementation
}
test "factorial basic cases" {
try std.testing.expectEqual(@as(u64, 120), factorial(5));
}// tests/zig_tests.rs
#[test]
fn test_math_zig_tests() {
let test_exe = get_test_exe_path("math");
let output = Command::new(&test_exe).output().unwrap();
assert!(output.status.success());
}Run tests:
cargo test # Automatically runs Rust and Zig tests๐ Learn More: docs/ZIG_TEST_INTEGRATION.md
๐ Seamless integration with existing C libraries through Zig wrappers
AutoZig supports calling C functions through Zig wrappers for Rust โ Zig โ C three-way interoperability:
// build.rs - Add C source files
use autozig_gen_build::Builder;
fn main() {
Builder::new()
.with_c_sources(&["src/math.c", "src/utils.c"])
.build()
.expect("Failed to build");
}// wrapper.zig - Zig wraps C functions
extern "c" fn c_add(a: i32, b: i32) i32;
export fn add(a: i32, b: i32) i32 {
return c_add(a, b);
}// main.rs - Rust calls through autozig
use autozig::zig;
zig! {
fn add(a: i32, b: i32) -> i32;
}
fn main() {
println!("{}", add(10, 20)); // Calls C through Zig
}Benefits:
- โ Leverage existing C libraries without rewriting
- โ Add Zig enhancements on top of C functions
- โ Type-safe FFI across all three languages
- โ Single build system manages everything
๐ Complete Example: examples/zig-c
๐ Import external
.zigfiles into your Rust project
Use the include_zig! macro to reference external .zig files:
use autozig::include_zig;
include_zig!("zig/math.zig", {
fn factorial(n: u32) -> u64;
fn fibonacci(n: u32) -> u64;
});
fn main() {
println!("5! = {}", factorial(5));
println!("fib(10) = {}", fibonacci(10));
}๐ Automatic conversion between Rust high-level types and Zig FFI-compatible types
| Rust Type | Zig Signature | Auto Conversion |
|---|---|---|
&str |
[*]const u8, usize |
โ |
&[T] |
[*]const T, usize |
โ |
&mut [T] |
[*]T, usize |
โ |
String |
[*]const u8, usize |
โ |
๐ค AutoZig manages the low-level ABI complexity with strict engineering rules.
Validation Rules:
- Struct Layout: Macros verify
#[repr(C)]on all shared structs at compile time. - Unsupported Types:
Bitfields,packed structs, and self-referential pointers are rejected. - Platform Mappings:
c_int/c_longโ๏ธ std.ffi.c_int(Zig)usizeโ๏ธ usize(pointer width aligned)
- Calling Convention: All
export fnusecallconv(.c)/extern "C".
To ensure soundness, AutoZig enforces these invariants:
- Thread Safety: Free callbacks (
free_fn) must be thread-safe (Send + Sync). - No Panic: Rust implementations called by Zig must never panic (unwinding across FFI is UB).
- Borrowing: Zig functions must not retain borrowed pointers (
[*]const) beyond the function call. - Ownership:
ZigBoxassumes exclusive ownership; aliasing it is UB.
Implement Rust traits with Zig backends
autozig! {
export fn calculator_add(a: i32, b: i32) i32 { return a + b; }
---
trait Calculator {
fn add(&self, a: i32, b: i32) -> i32 => calculator_add;
}
}
let calc = Calculator::default();
assert_eq!(calc.add(2, 3), 5);autozig! {
export fn hasher_new() *anyopaque { /* ... */ }
export fn hasher_update(ptr: *anyopaque, data: [*]const u8, len: usize) void { /* ... */ }
export fn hasher_finalize(ptr: *anyopaque) u64 { /* ... */ }
export fn hasher_destroy(ptr: *anyopaque) void { /* ... */ }
---
trait Hasher opaque {
fn new() -> Self => hasher_new;
fn update(&mut self, data: &[u8]) => hasher_update;
fn finalize(&self) -> u64 => hasher_finalize;
fn destroy(self) => hasher_destroy;
}
}๐ Learn More: docs/TRAIT_SUPPORT_DESIGN.md
All examples are fully tested and ready to run:
- structs - Structure bindings
- enums - Enum types and Result/Option
- complex - Complex nested types
- smart_lowering - Automatic type conversion
- external - External Zig files with
include_zig! - trait_calculator - Trait implementation (ZST)
- trait_hasher - Trait implementation (Opaque Pointer)
- security_tests - Memory safety tests
- generics - Generic monomorphization (Phase 3)
- async - Async FFI with spawn_blocking (Phase 3)
- zig-c - C + Zig + Rust three-way interop
- stream_basic - Stream support (Phase 4)
- simd_detect - SIMD detection (Phase 4)
- zero_copy - Zero-copy optimization (Phase 4)
- wasm_filter - WebAssembly image filter with SIMD optimization (Phase 5) ๐
- leak_test - Memory Safety Protocol stress verification (Phase 6) ๐ก๏ธ
๐ NEW! AutoZig now supports full C + Zig + Rust three-way interoperability!
The zig-c example demonstrates a complete calling chain: Rust โ Zig โ C
use autozig::zig;
zig! {
// Zig wraps C functions and adds enhancements
fn add(a: i32, b: i32) -> i32; // C: c_add()
fn power(base: i32, exp: u32) -> i32; // Zig: uses c_multiply()
fn sum_array(arr: &[i32]) -> i32; // C: c_sum_array()
fn average(arr: &[i32]) -> f64; // Hybrid: C sum + Zig float math
}
fn main() {
// All tests passing: 4/4 unit tests โ
println!("{}", add(10, 20)); // 30
println!("{}", power(2, 10)); // 1024
println!("{}", sum_array(&[1,2,3,4,5])); // 15
println!("{}", average(&[1,2,3,4,5])); // 3.0
}Key Features:
- โ C Integration: Use existing C libraries through Zig wrappers
- โ
Smart Lowering:
&[i32]and&strautomatically converted toptr + len - โ Type Safety: Full type checking across all three languages
- โ Zero Overhead: Direct FFI calls with no runtime cost
- โ
Build System: Single
build.rswithwith_c_sources()API
Architecture:
Rust (safe API)
โ FFI call
Zig (wrapper + enhancements)
โ extern "c"
C (low-level implementation)
๐ Learn More: examples/zig-c/README.md
Run all examples at once:
cd examples
./verify_all.shOutput:
======================================
Verification Results Summary
======================================
Total: 15 examples (14 standard + 1 WASM)
Success: 15
Failed: 0
Skipped: 0
[โ] All examples verified successfully! ๐
๐ Learn More: examples/README.md
AutoZig follows a three-stage pipeline for seamless Rust-Zig interop:
โโโโโโโโโโโโโโโ
โ Rust Code โ
โ with โ
โ autozig! โ
โโโโโโโโฌโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Stage 1: Parsing (Compile Time) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โข Scan .rs files for autozig! macros โ
โ โข Extract Zig code โ
โ โข Parse Rust signatures โ
โ โข Detect generics & async โ
โโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Stage 2: Build (build.rs) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โข Compile Zig โ static library (.a) โ
โ โข Generate monomorphized versions โ
โ โข Link with Rust binary โ
โโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Stage 3: Macro Expansion โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โข Generate safe Rust wrappers โ
โ โข Handle &str โ (ptr, len) conversion โ
โ โข Generate async spawn_blocking โ
โ โข Include FFI bindings โ
โโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโ
โ Safe Rust โ
โ API โ
โโโโโโโโโโโโโโโ
autozig/
โโโ src/lib.rs # Main library
โโโ parser/ # Macro input parser
โ โโโ src/lib.rs # Parse generics & async
โโโ macro/ # Procedural macro
โ โโโ src/lib.rs # Code generation (Phase 3)
โโโ engine/ # Core build engine
โ โโโ scanner.rs # Source code scanner
โ โโโ zig_compiler.rs # Zig compiler wrapper
โ โโโ type_mapper.rs # Type conversion logic
โโโ gen/build/ # Build script helpers
โโโ examples/ # 14 working examples
โ โโโ verify_all.sh # Batch verification script
โ โโโ README.md # Examples documentation
โโโ docs/ # Technical documentation
|
| Component | Version | Notes |
|---|---|---|
| Rust | 1.77+ | Workspace features required |
| Zig | 0.15+ | Must be in PATH |
| Tokio | 1.0+ | Required for async examples |
| Feature | autocxx (C++) | autozig (Zig) |
|---|---|---|
| Target Language | C++ | Zig |
| Binding Generator | bindgen + cxx | bindgen |
| Safe Wrappers | โ | โ |
| Inline Code | โ | โ |
| Generics Support | โ | โ |
| Async Support | โ | โ |
| Stream Support | โ | โ |
| Zero-Copy | โ | โ |
| SIMD Optimization | โ | โ |
| Build Complexity | High | Medium |
| Type Safety | Strong | Strong |
| Zig Type | Rust Type | Notes |
|---|---|---|
i8, i16, i32, i64 |
i8, i16, i32, i64 |
โ Direct mapping |
u8, u16, u32, u64 |
u8, u16, u32, u64 |
โ Direct mapping |
f32, f64 |
f32, f64 |
โ Direct mapping |
bool |
u8 |
|
[*]const u8 |
*const u8 |
๐ง Raw pointer |
[*]const u8 + len |
&[u8] |
๐ก๏ธ With safe wrapper |
Contributions are welcome! This is an experimental project exploring Rust-Zig interop.
Ways to contribute:
- ๐ Report bugs and issues
- ๐ก Suggest new features
- ๐ Improve documentation
- ๐ง Submit pull requests
- ๐ฏ Add new examples
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
- ๐ก Inspired by autocxx
- ๐จ Built on bindgen
- โก Leverages the excellent Zig language
- ๐ Async architecture inspired by Tokio best practices
โ Phase 1-6 Complete! - AutoZig is feature-complete with Memory Safety Protocol & WebAssembly support!
Current Status:
- โ Phase 1: Basic FFI bindings (100%)
- โ Phase 2: Smart Lowering & Traits (100%)
- โ Phase 3: Generics & Async (100%)
- โ Phase 4: Stream, Zero-Copy & SIMD (100%)
- โ Phase 5: WebAssembly Support (100%) ๐
- โ Phase 6: Memory Safety Protocol (100%) ๐ก๏ธ
Statistics:
- ๐ฆ 16 working examples
- โ 40+ tests passing (100%)
- ๐ 22+ documentation files
- ๐ Full WASM support with static linking
- ๐ก๏ธ Zero Unsafe in User Code Verified
- ๐ Production ready
- ๐ ไฝฟ็จๆ็จ (Tutorial) - ๅฎๆด็ไธญๆไฝฟ็จๆ็จ
- ๐ฏ Quick Start - Get started in 5 minutes
- ๐ Design Notes - Architecture overview
- ๐ Implementation Summary - Technical deep dive
- ๐ท Phase 3: Generics Design
- โก Phase 3: Async Design
- โ Phase 3: Complete Status
- ๐ Phase 4: Stream Design
- ๐ Phase 4: Implementation Status
- ๐ฏ Phase 4.2: Advanced Features
- ๐งช Zig Test Integration
- ๐บ๏ธ Trait Support Design
- ๐ก๏ธ Security Best Practices
- ๐ Zero Unsafe Achievement
- ๐ Feature Summary (Chinese) - Complete feature checklist
- ๐ Examples Directory - 14 working examples
- ๐ Examples README - Detailed guide
- ๐ Batch Verification - Test all examples
Made with โค๏ธ for the Rust and Zig communities
โญ Star on GitHub โข ๐ Report Issues โข ๐ Read Docs
