Skip to content

Introduction to Rayforce-RS

rayforce provides convenient, high-performance Rust bindings for RayforceDB v2 — a lightweight, SIMD-vectorized columnar database. It lets you build values, tables, and queries with idiomatic Rust and run them inside the RayforceDB runtime with little-to-no practical overhead.

use rayforce::{col, sum, Runtime, Table, Value};

let _rt = Runtime::new()?;                        // one live runtime per process

let t = Table::new(
    &["sym", "price", "size"],
    &[
        Value::sym_vec(&["AAPL", "MSFT", "AAPL", "GOOG"]),
        Value::vec(&[100.0f64, 200.0, 110.0, 300.0]),
        Value::vec(&[10i64, 20, 30, 40]),
    ],
)?;

// select total:sum size by sym from t where price > 150.0
let totals = t
    .select()
    .agg("total", sum(col("size")))
    .filter(col("price").gt(150.0))
    .by("sym")
    .execute()?;

println!("{totals}");
# Ok::<(), rayforce::RayError>(())

Direct FFI, no shim

The bindings link the RayforceDB core (librayforce.a) directly and call its C API natively. There is no separate process, no IPC shim, and no serialization between your Rust code and the engine for in-process work.

Zero-copy where it counts

A Value is a thin, reference-counted handle onto memory owned by the engine. Building a numeric column is a single memcpy (Value::vec(&[T])), and reading it back is zero-copy: a numeric column is exposed as a &[T] slice via value.as_slice::<T>() rather than copied element-by-element. Cloning a Value only bumps a refcount.

The result is a thin, ergonomic safe layer over a fast vectorized engine — without the cost of crossing a process boundary.

30-second quickstart

Every program starts by creating a single live Runtime. It is an RAII guard: keep it alive for as long as you touch any Value.

use rayforce::{col, Runtime, Table, Value};

let _rt = Runtime::new()?;

// Build a table from typed columns.
let trades = Table::new(
    &["sym", "price", "size"],
    &[
        Value::sym_vec(&["AAPL", "MSFT", "AAPL"]),
        Value::vec(&[100.0f64, 200.0, 110.0]),
        Value::vec(&[10i64, 20, 30]),
    ],
)?;

// Filter and project with the fluent query DSL.
let big = trades
    .select()
    .filter(col("size").gt(15i64))
    .execute()?;

println!("{big}");
# Ok::<(), rayforce::RayError>(())

Integer literals default to i32

A bare literal like 42 is i32 in Rust. Annotate the element type when you build non-i32 vectors or atoms — Value::vec(&[1i64, 2, 3]), col("size").gt(15i64).

Next steps

  • Installation — prerequisites and how to build against a local RayforceDB core.
  • Technical Details — the two-crate workspace, the Value RAII model, and the single-thread runtime.
  • Documentation — data types, tables, the query guide, IPC, and serialization.