---
title: "Why We Chose Rust for HeliosDB"
description: "Memory safety without garbage collection, fearless concurrency, and zero-cost abstractions. Here's why Rust was the only choice."
date: 2025-12-12
updated: 2025-12-12
tag: "Engineering"
canonical: https://www.heliosdb.com/blog/why-we-chose-rust-for-heliosdb.html
---

# Why We Chose Rust for HeliosDB

When we started building HeliosDB, the language choice was one of the most consequential decisions we made. We chose Rust, and it has shaped everything from our architecture to our hiring to our competitive advantage. Here's why Rust was the only realistic choice for a modern database engine.

## The Database Language Requirements

A database engine has some of the most demanding requirements of any software:

**Memory Safety Without GC Pauses:** Databases hold critical data. Memory corruption bugs don't just crash your server — they corrupt your data. But garbage collector pauses are equally unacceptable when you're trying to guarantee sub-millisecond query latency.

**Fearless Concurrency:** Modern databases handle thousands of concurrent connections, each potentially accessing shared data structures. Race conditions in a database are catastrophic — they lead to silent data corruption that may not be discovered for months.

**Zero-Cost Abstractions:** We need high-level abstractions (trait-based interfaces, iterators, closures) to manage the complexity of a 193-crate codebase. But we can't afford the runtime overhead of dynamic dispatch and heap allocation for every operation.

## The Rust Advantage in Practice

**Ownership and Borrowing for Data Integrity:** Consider our MVCC implementation. Multiple transactions can read the same data simultaneously, but only one can write. Rust's borrow checker enforces this at compile time — it's literally impossible to have a data race in our MVCC layer.

**Async/Await for I/O Concurrency:** HeliosDB uses `tokio 1.40` with full features as our async runtime. Every I/O operation — disk reads, network communication, replication — is non-blocking. A single HeliosDB process efficiently handles tens of thousands of concurrent connections.

```
# Our core dependency stack (from Cargo.toml)
[workspace.dependencies]
tokio = { version = "1.40", features = ["full"] }     # Async runtime
rocksdb = { version = "0.22", features = ["snappy", "zstd"] }  # Storage
crossbeam = { version = "0.8" }                        # Concurrent data structures
parking_lot = { version = "0.12" }                     # Fast synchronization
lz4 = { version = "1.28" }                            # Compression
zstd = { version = "0.13.3" }                          # Compression
```

## Where We Use Unsafe (Carefully)

Rust's `unsafe` keyword doesn't mean "dangerous" — it means "the programmer is asserting safety invariants that the compiler can't verify." In HeliosDB, we use `unsafe` in exactly one subsystem: SIMD vector operations in `heliosdb-compute`.

We have 58 `unsafe` blocks across the entire codebase, every single one documented with a `// SAFETY:` comment explaining why the invariant holds. These are concentrated in our AVX2/SSE4 SIMD kernels for vector distance computation, where the compiler can't prove that slice indices are in bounds (because they depend on runtime-known dimensions).

```
// Example from our SIMD acceleration (heliosdb-compute)
// SAFETY: We verify that codes.len() >= num_subquantizers
// and distance_table.len() >= num_subquantizers * codebook_size
// before calling this function. The gather operations
// use validated indices within the codebook_size range.
unsafe fn asymmetric_distance_avx2(
    codes: &[u8],
    distance_table: &[f32],
    num_subquantizers: usize,
    codebook_size: usize,
) -> f32 { ... }
```

Zero `unsafe` blocks in our storage engine, SQL parser, query optimizer, consensus layer, or any user-facing code path. The safety-critical code is safe by construction.

## Cryptographic Safety

For encryption, Rust's `zeroize` crate ensures that sensitive data (keys, passwords) is overwritten in memory when dropped. This prevents secrets from lingering in deallocated memory:

```
use zeroize::Zeroize;

pub struct EncryptionKey {
    key: Vec<u8>,  // Automatically zeroed on drop
}

// We use ring 0.17 and aws-lc-rs for crypto operations
// aes-gcm 0.10 for authenticated encryption
// chacha20poly1305 0.10 for software-optimized encryption
// argon2 0.5 for memory-hard key derivation
```

## Compile-Time Guarantees

Our Cargo.toml enforces strict linting across all 193 crates:

```
[workspace.lints.clippy]
all = { level = "warn", priority = -1 }

[profile.release]
lto = true           # Link-time optimization across all crates
codegen-units = 1    # Maximum optimization
opt-level = 3        # Highest optimization level
```

LTO across 193 crates is expensive at compile time but enables the compiler to inline across crate boundaries, eliminate dead code, and optimize hot paths that span multiple subsystems.

## The Result

Rust has given us a database that combines the performance of C/C++ databases (no GC pauses, zero-cost abstractions, SIMD acceleration) with the safety guarantees that modern infrastructure demands (no buffer overflows, no data races, no use-after-free). Our 193-crate workspace is manageable precisely because Rust's type system and borrow checker catch entire categories of bugs at compile time.

Is Rust harder to learn than Go or Java? Yes. Is the compile time longer? Absolutely. But for a database engine where correctness is non-negotiable and performance determines viability, Rust is the only language that doesn't force you to choose between safety and speed.
