Skip to content

Building a VST Host in Rust: Lessons from 1,400 Tests

Building a VST plugin host is one of the harder problems in audio software. You’re loading arbitrary third-party code into your process, talking to it through a C++ COM interface, processing real-time audio with strict latency constraints, and dealing with plugins that crash, hang, or violate every API contract in the spec.

I built one in Rust. Here’s what I learned.

The conventional wisdom is that audio infrastructure means C++. The VST3 SDK is C++, most DAWs are C++, and the ecosystem assumes you’re writing C++.

But Rust offers real advantages for this kind of systems programming:

  • Memory safety without garbage collection — No GC pauses in the audio thread, but also no use-after-free bugs when managing plugin instances
  • Fearless concurrency — Audio hosts are inherently multi-threaded (audio thread, UI thread, plugin scanning thread). Rust’s ownership model prevents data races at compile time.
  • Excellent FFI — Rust’s C FFI is zero-cost, and COM interfaces can be modelled with Rust traits
  • Pattern matching — VST3’s error handling is done through tresult codes. Rust’s match makes these exhaustive and hard to ignore.

The host follows a process-per-plugin model. Each plugin runs in its own child process, communicating with the host via shared memory and IPC. This gives you:

  1. Crash isolation — A misbehaving plugin can’t take down the host or other plugins
  2. Security boundaries — Plugins can’t access host memory or other plugins’ state
  3. Clean shutdown — If a plugin hangs, you kill the process and recover

The audio routing uses a DAG (directed acyclic graph) with topological sorting, so signal flow is resolved at graph-change time rather than per-sample. This keeps the audio callback lean.

VST3 uses Microsoft’s Component Object Model (COM) for its plugin interface. From Rust, this means:

  • Implementing IUnknown-style reference counting with AddRef/Release
  • Vtable-compatible struct layouts using #[repr(C)]
  • Querying for interfaces via queryInterface with GUID matching

Rust’s trait system maps surprisingly well to COM interfaces. Each VST3 interface becomes a Rust trait, and the vtable dispatch is handled by a macro that generates the correct memory layout.

// Simplified — the actual implementation handles all VST3 interfaces
#[repr(C)]
struct PluginProxy {
vtable: *const IAudioProcessorVtbl,
ref_count: AtomicU32,
// ...
}

The key insight: Rust’s #[repr(C)] guarantees and its unsafe blocks give you the same low-level control as C++, but all the surrounding code benefits from Rust’s safety guarantees.

The host has over 1,400 tests covering:

  • Unit tests — Individual components (graph routing, parameter mapping, COM reference counting)
  • Integration tests — Loading real plugins, processing audio, verifying output
  • Stress tests — Rapid plugin load/unload cycles, concurrent audio processing
  • Crash recovery tests — Deliberately crashing plugins and verifying the host recovers

The test count isn’t the point — what matters is that Rust’s type system catches entire categories of bugs at compile time, so the tests focus on behaviour rather than memory correctness.

The real-time audio callback can’t allocate memory, can’t lock mutexes (that might be held by other threads), and can’t do I/O. In Rust, you can enforce this with careful API design — types that are !Send can’t accidentally move to the audio thread.

2. Plugin compatibility is the real problem

Section titled “2. Plugin compatibility is the real problem”

The hardest part isn’t the host architecture — it’s dealing with plugins that don’t follow the spec. Some plugins expect to be called from the main thread. Some crash if you pass null for optional parameters. Some leak memory. The process-per-plugin model helps, but you still need defensive programming at every boundary.

Yes, Rust compiles slower than C++. But the amount of debugging time saved — especially for concurrency bugs and memory issues — more than compensates. The “if it compiles, it works” intuition holds up well for audio infrastructure.

A production-grade VST host with process isolation, DAG-based routing, and comprehensive testing — all written in safe Rust (with unsafe only at the COM boundary layer). The codebase is maintainable, the audio thread is clean, and adding new features doesn’t require heroic debugging sessions.


Building something that needs low-level audio infrastructure? I bring this same approach to client projects — safe, tested, and production-ready. View services → or let’s talk about your project →.