DEV Community

Samuel Aondo
Samuel Aondo

Posted on

Building Zano: A Node.js-like Runtime in Rust

From concept to crates.io - How I built a JavaScript-compatible runtime that brings familiar Node.js patterns to the Rust ecosystem


The Journey Begins

As a developer who loves both JavaScript's expressiveness and Rust's performance guarantees, I've always wondered: "What if we could have the best of both worlds?"

Today, I'm excited to share Zano v0.1.1 - a Node.js-like runtime built entirely in Rust that makes this dream a reality.

Try it now: cargo install zano

Crates.io: https://crates.io/crates/zano

GitHub: https://github.com/sazalo101/zano

What is Zano?

Zano is a high-performance backend runtime that provides a familiar Node.js development experience while leveraging Rust's memory safety and zero-cost abstractions. Think of it as "Node.js syntax with Rust performance."

// This is valid Zano code!
const fs = require('fs')

try {
    fs.writeFile('hello.txt', 'Hello from Zano!')
    let content = fs.readFile('hello.txt')
    console.log('File content:', content)
} catch (error) {
    console.error('Operation failed:', error)
}
Enter fullscreen mode Exit fullscreen mode

The Technical Challenge

Building a JavaScript-compatible runtime in Rust presented several fascinating challenges:

1. Language Design

Creating a parser that understands JavaScript syntax while maintaining Rust's type safety required careful design decisions. I built a custom lexer and recursive descent parser that handles:

  • Variables (let, const, var)
  • Functions with parameters and return values
  • Objects and arrays with familiar access patterns
  • Control flow (if/else, while, try/catch)

2. Async Runtime Architecture

Node.js's event loop is legendary for its performance. I replicated this using Tokio:

pub struct ZanoRuntime {
    globals: Arc<RwLock<HashMap<String, ZanoValue>>>,
    functions: Arc<RwLock<HashMap<String, Arc<dyn ZanoFunction>>>>,
    modules: Arc<RwLock<HashMap<String, ZanoValue>>>,
}
Enter fullscreen mode Exit fullscreen mode

3. Module System

Implementing require() functionality meant building a complete module resolution system with built-in modules like:

const fs = require('fs')      // File system operations
const http = require('http')  // HTTP client/server
const path = require('path')  // Path manipulation
// Global console object available everywhere
Enter fullscreen mode Exit fullscreen mode

Key Features That Make Zano Special

Familiar Syntax

let user = {name: "Alice", age: 30}
let numbers = [1, 2, 3, 4, 5]

function calculateAge(birthYear) {
    return 2025 - birthYear
}

console.log("User:", user.name, "Age:", calculateAge(1995))
Enter fullscreen mode Exit fullscreen mode

Robust Error Handling

try {
    console.log("Attempting risky operation...")
    throw "Something went wrong!"
} catch (error) {
    console.error("Caught:", error)
}
console.log("Program continues safely!")
Enter fullscreen mode Exit fullscreen mode

Package Management

zano init                    # Creates package.json
zano install lodash         # Installs dependencies  
zano run start             # Runs npm scripts
Enter fullscreen mode Exit fullscreen mode

Multiple Execution Modes

zano script.zn              # Run files
zano -e "console.log('Hi')" # Direct evaluation  
zano -i                     # Interactive REPL
Enter fullscreen mode Exit fullscreen mode

Performance Comparison

Aspect Zano Node.js
Memory Safety ✅ Compile-time ❌ Runtime errors possible
Startup Time ⚡ Instant 🐌 ~50ms
Memory Usage 📦 Minimal 📊 Higher baseline
Concurrency Tokio-powered libuv-based

Real-World Examples

File Processing Pipeline

// file-processor.zn
const fs = require('fs')

console.log("File Processing Pipeline")

try {
    let data = "user1,admin\nuser2,member\nuser3,guest"
    fs.writeFile('users.csv', data)

    let content = fs.readFile('users.csv')
    let processed = "Processed: " + content

    fs.writeFile('processed.txt', processed)
    console.log("✅ Processing complete!")

} catch (error) {
    console.error("❌ Processing failed:", error)
}
Enter fullscreen mode Exit fullscreen mode

Data Analysis Script

// analytics.zn
let jan = {month: "Jan", sales: 1000, costs: 800}
let feb = {month: "Feb", sales: 1200, costs: 900}

function calculateProfit(record) {
    return {
        month: record.month, 
        profit: record.sales - record.costs
    }
}

let results = [calculateProfit(jan), calculateProfit(feb)]
let totalProfit = results[0].profit + results[1].profit

console.log("Total Profit: $" + totalProfit)
// Output: Total Profit: $500
Enter fullscreen mode Exit fullscreen mode

Technical Architecture Deep Dive

Parser Implementation

The heart of Zano is its parser, built as a recursive descent parser with careful attention to JavaScript semantics:

pub enum Expression {
    Literal(ZanoValue),
    Identifier(String),
    Binary { left: Box<Expression>, operator: BinaryOp, right: Box<Expression> },
    Call { callee: Box<Expression>, args: Vec<Expression> },
    Member { object: Box<Expression>, property: String },
    Array(Vec<Expression>),
    Object(Vec<(String, Expression)>),
    // ... more variants
}
Enter fullscreen mode Exit fullscreen mode

Runtime Execution

The runtime uses Tokio's async capabilities to handle JavaScript's event-driven nature:

async fn evaluate_expression(&self, expression: Expression) -> Result<ZanoValue> {
    match expression {
        Expression::Call { callee, args } => {
            // Handle function calls including member functions like console.log
            let function_name = self.resolve_function_name(callee)?;
            let func = self.functions.read().await.get(&function_name)?;
            func.call(args).await
        }
        // ... handle other expression types
    }
}
Enter fullscreen mode Exit fullscreen mode

Development Lessons Learned

1. Start Simple, Iterate

I began with basic arithmetic and gradually added features. This approach prevented overwhelming complexity and allowed thorough testing at each stage.

2. Embrace Rust's Type System

Initially, I fought against Rust's borrow checker. Eventually, I learned to design with ownership in mind, leading to cleaner, more performant code.

3. Community Feedback is Gold

Testing examples from the README revealed edge cases I hadn't considered. User perspective is invaluable.

4. Documentation-Driven Development

Writing examples for the README helped me understand what the API should look like from a user's perspective.

Performance Benefits in Practice

Here's a simple benchmark comparing Zano to Node.js for a CPU-intensive task:

// fibonacci.zn / fibonacci.js
function fibonacci(n) {
    if (n <= 1) return n
    return fibonacci(n - 1) + fibonacci(n - 2)
}

console.log("Fibonacci(35):", fibonacci(35))
Enter fullscreen mode Exit fullscreen mode

Results (on my machine):

  • Zano: ~1.2s with instant startup
  • Node.js: ~1.8s + 50ms startup overhead

Note: This is a synthetic benchmark. Real-world performance varies.

What's Next for Zano?

The v0.1.1 release is just the beginning! Here's what's planned:

Version 1.1.0

  • [ ] Full async/await support with proper Promise handling
  • [ ] HTTP server implementation with routing
  • [ ] Module bundling system
  • [ ] Package registry integration

Version 1.2.0

  • [ ] Class syntax support
  • [ ] Destructuring assignment
  • [ ] Template literals with interpolation
  • [ ] JSON parsing utilities
  • [ ] Comprehensive standard library

Version 2.0.0

  • [ ] TypeScript-like static typing (optional)
  • [ ] WebAssembly module support
  • [ ] Built-in testing framework
  • [ ] Advanced debugging tools

Contributing and Community

Zano is open source and welcomes contributions! Whether you're:

  • Finding bugs (please report them!)
  • Suggesting features (what would make Zano more useful?)
  • Improving documentation (clarity is key!)
  • Contributing code (PRs welcome!)

Get involved:

Installation and Getting Started

Ready to try Zano? Here's how to get started:

# Install Zano
cargo install zano

# Create your first project
zano init
echo 'console.log("Hello, Zano!")' > hello.zn
zano hello.zn

# Try the REPL
zano -i

# Quick evaluation
zano -e 'let x = 5; let y = 10; console.log("Sum:", x + y)'
Enter fullscreen mode Exit fullscreen mode

Conclusion

Building Zano has been an incredible journey that reinforced my belief that the Rust ecosystem can accommodate diverse programming paradigms while maintaining its core values of safety and performance.

Zano proves that you don't have to choose between familiar syntax and systems programming benefits. You can have both.

The future of backend development is bright, and I believe tools like Zano that bridge different programming communities will play a crucial role in that future.


Try Zano Today!

cargo install zano
Enter fullscreen mode Exit fullscreen mode

Links:


What are your thoughts on bringing JavaScript-like syntax to Rust? Have you tried Zano? I'd love to hear your feedback and experiences in the comments!

Tags: #rust #javascript #nodejs #opensource #programming #webdev #performance #memorysafety #async #runtime


Follow me for more updates on Zano's development and other Rust projects!

Top comments (0)