Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

How It Compares

datalogic-rs is a JSONLogic engine: rules are JSON data, evaluated by one Rust core that is wrapped for eight languages plus the browser. This page positions it against the alternatives people most often evaluate it beside. For raw numbers and methodology, see Performance and the benchmark matrix.

Dimensiondatalogic-rsjson-logic-jsjson-logic-enginejsonlogic-rsGoRules ZENCEL
FormatJSONLogic (JSON data)JSONLogic (defines the standard)JSONLogic supersetJSONLogicProprietary JDM + Zen expression languageCEL expression grammar (not JSON)
LanguagesOne core, official bindings for Rust, Node, WASM, Python, Go, Java, .NET, PHPJS core; other languages are separate community portsJS/TS onlyRust (single crate, Python/WASM wrappers exist but stale)Rust core with several bindingsGo/Java mature, others varying
Standard compliancePasses the official JSONLogic test suite, plus opt-in extensionsThe reference implementationSuperset with minor deviationsPasses core suiteNot JSONLogicOwn spec
SandboxingNo eval, no I/O, core forbids unsafe codeNo evalNo evalNo evalFunction nodes execute JavaScript (QuickJS)Non-Turing-complete, strong formal story
ToolingReact visual editor, step-through trace debugger, online playgroundPlay pageNone officialNone officialJDM editor + commercial BRMSCommunity playgrounds
ExtensibilityCustom operators per host languageCustom ops in JSCustom ops (incl. async) in JSLimitedCustom nodesExtension functions per environment

One naming collision deserves a call-out: jsonlogic-rs on crates.io is bestowinc/json-logic-rs, a different project from this one despite the near-identical name. This crate is datalogic-rs. The two are compared directly below.

One engine vs. N ports

JSONLogic’s core promise is portability: rules are plain JSON, so any language can evaluate them. The ecosystem’s structural problem is how that promise gets delivered. json-logic-js is the JavaScript reference, and every other language depends on an independent reimplementation: separate community ports for Python, PHP, Go, Ruby, Java, and more, each with its own maintainer, release cadence, and bug tail. The ports drift. Truthiness edge cases, type coercion, null handling, and error behavior diverge one patch release at a time, and a rule that passes tests in your Node service can quietly evaluate differently in your Python batch job.

datalogic-rs inverts the model: one Rust core, compiled into every runtime. The Node addon, the WASM package, the Python wheel, and the Go, Java, .NET, and PHP bindings all embed the same engine; none of them reimplements a single operator. Semantic parity across languages is a build artifact, not a hope, and two concrete checks keep it that way:

  • The same 1,565-case conformance battery (54 suites) runs against the core in CI. Every binding ships the exact engine those cases validated, so there is no per-language test matrix to fall behind.
  • The flagd fractional operator is byte-compatible with the canonical Go evaluator’s MurmurHash3 bucketing, so even hash-based percentage rollouts put the same user in the same bucket in every language.

When to choose which

datalogic-rs fits when your rules should be data: stored in a database column, diffed in review, generated by a UI, and evaluated with identical semantics on the client and every backend service. Its two most distinctive properties are one engine shared binary-identically across eight languages, and an official visual debugger for the standard. Each alternative below is genuinely the better pick in its own lane.

json-logic-js

The reference implementation, and the project that defines the JSONLogic standard. datalogic-rs passes the same official test suite, so existing json-logic-js rules run unchanged.

Choose it when: you only need JavaScript, you value the smallest and most battle-tested dependency, and the reference engine’s performance is comfortable at your rule volume. As the standard’s source of truth, it is the canonical choice for a JS-only stack.

Choose datalogic-rs when: the same rules must also run outside JavaScript, evaluation is hot enough to show up in profiles (the native engine measures about 84x faster across the shared benchmark suites), or you want the visual debugger and playground.

Migrating is close to a package swap; the full mapping is in Coming from json-logic-js.

json-logic-engine

The fast, actively maintained JavaScript engine, and the credible JS-side alternative on speed: its compiled mode is the only non-Rust subject in the same order of magnitude as datalogic-rs in the benchmark matrix.

Choose it when: your stack is JS/TS end to end and you want JS-native ergonomics, above all async custom operators, which a compiled-core engine cannot offer as naturally.

Choose datalogic-rs when: you need one core across many languages, native (non-JS) bindings, closer adherence to the reference semantics, or the visual tooling. Rules that stay inside the shared JSONLogic standard carry over unchanged.

jsonlogic-rs (bestowinc)

The identically named neighbor on crates.io, and the comparison people search for most. jsonlogic-rs is a single-crate Rust implementation of core JSONLogic with an apply(&Value, &Value) API: there is no compile step, so every call re-walks the rule JSON. Python and WASM wrappers exist in the same repository but have not seen recent releases. It is a reasonable, small dependency for occasional evaluation of standard rules.

Choose it when: you want a minimal one-function crate, your rules stick to the core standard, and evaluations are infrequent enough that per-call rule walking does not matter.

Choose datalogic-rs when: evaluation is hot (compile-once evaluation measures about 31x faster geomean on the shared suites), you need the extended operators (datetime, string, try/throw, flagd), custom operators, tracing, or any of the non-Rust bindings. Both engines speak JSONLogic, so rules carry over unchanged; switching is confined to the Rust call sites, and the quick start shows the equivalent one-liner.

GoRules ZEN

A business-rules platform built around decision tables and graphs (a proprietary JDM format), with a commercial editor. It solves a bigger problem than expression evaluation.

Choose it when: you want a full BRMS abstraction for business users: decision tables, graph orchestration, and a polished commercial editing experience on top of the engine.

Choose datalogic-rs when: you want a lighter embedding around an open standard, or a stricter sandbox: ZEN’s function nodes run JavaScript (QuickJS), while datalogic-rs executes no rule-supplied code at all.

CEL

Common Expression Language owns the “safe expression language” space in the Kubernetes and Envoy ecosystems, with a strong non-Turing-complete guarantee and a formal spec.

Choose it when: you want an expression grammar (not JSON) and its ecosystem integrations, or you already live in a CEL-native environment such as Kubernetes admission control.

Choose datalogic-rs when: rules should be plain JSON: storable, diffable, and UI-generatable with no grammar or parser to maintain, plus an official visual editor for the people who write the rules.

The numbers

Geomean execution time across 51 benchmark suites (Apple M2 Pro; median of 3 samples; ratios are pairwise shared-suite geomeans; methodology in the benchmark matrix).

datalogic-rs (native Rust)              | 10.3 ns  (■) 1x
json-logic-engine (JS, compiled)        | 63.3 ns  (■■■■■■) 7.0x
json-logic-engine (JS, interpreted)     | 234.8 ns (■■■■■■■■■■■■■■■■■■■■■■■) 25.8x
jsonlogic-rs (bestowinc Rust engine)    | 264.2 ns (■■■■■■■■■■■■■■■■■■■■■■■■■■) 28.1x
json-logic-js (Reference JS library)    | 465.1 ns (■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■) 83.6x

One honest caveat: the WASM build under Node measures 900.5 ns (88x native), so on Node servers the native binding is the fast path; the WASM package is for browsers and edge runtimes.

Proof: the same rule everywhere

One rule, one datum, eight runtimes:

{
  "and": [
    {">=": [{"var": "age"}, 18]},
    {"==": [{"var": "status"}, "active"]}
  ]
}

With data {"age": 25, "status": "active"}, every binding returns true:

RuntimeOne-line call
Rustdatalogic_rs::eval_str(rule, data)?
Node.jsapply(rule, data)
Browser (WASM)evaluate(rule, data, false)
Pythonapply(rule, data)
Godatalogic.Apply(rule, data)
Javaengine.apply(rule, data)
C#engine.Apply(rule, data)
PHP$engine->apply($rule, $data)

Not eight lookalike engines: the same compiled core behind eight call signatures. Try the rule live in the playground, then pick your language in Installation.