Back to Documentation

Rust SDK

Memory-safe, zero-cost abstractions for entropyDB with async/await

Overview

The Rust SDK provides:

  • Memory Safety: No null pointers or data races
  • Zero-Cost: Abstractions with no runtime overhead
  • Async/Await: Tokio-based async runtime
  • Type Safety: Compile-time query validation
  • Performance: Optimized for high throughput

Installation

# Add to Cargo.toml
[dependencies]
entropydb = "1.0"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Basic Usage

use entropydb::{Client, Error};
use serde::{Deserialize, Serialize};
use tokio;

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: i64,
    username: String,
    email: String,
    created_at: chrono::NaiveDateTime,
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    // Connect to entropyDB
    let client = Client::builder()
        .host("localhost")
        .port(5432)
        .database("mydb")
        .user("admin")
        .password("password")
        .pool_size(20)
        .build()
        .await?;
    
    // Create table
    client.execute(
        "CREATE TABLE IF NOT EXISTS users (
            id SERIAL PRIMARY KEY,
            username TEXT NOT NULL,
            email TEXT UNIQUE,
            created_at TIMESTAMP DEFAULT NOW()
        )",
        &[],
    ).await?;
    
    // Insert data
    let rows_affected = client.execute(
        "INSERT INTO users (username, email) VALUES ($1, $2)",
        &[&"alice", &"alice@example.com"],
    ).await?;
    
    println!("Inserted {} row(s)", rows_affected);
    
    // Query single row
    let row = client.query_one(
        "SELECT id, username, email, created_at FROM users WHERE username = $1",
        &[&"alice"],
    ).await?;
    
    let user = User {
        id: row.get(0),
        username: row.get(1),
        email: row.get(2),
        created_at: row.get(3),
    };
    
    println!("User: {:?}", user);
    
    // Query multiple rows
    let rows = client.query(
        "SELECT id, username, email FROM users WHERE email LIKE $1",
        &[&"%@example.com"],
    ).await?;
    
    for row in rows {
        let username: String = row.get(1);
        println!("Username: {}", username);
    }
    
    Ok(())
}

Type-Safe Queries with Macros

use entropydb::query;

// Type-safe query macro
let users: Vec<User> = query!(
    client,
    "SELECT id, username, email, created_at FROM users WHERE email LIKE $1",
    "%@example.com"
).fetch_all().await?;

// Compile-time SQL validation
let user: User = query!(
    client,
    "SELECT * FROM users WHERE id = $1",
    user_id
).fetch_one().await?;

// Query builder with type safety
use entropydb::QueryBuilder;

let query = QueryBuilder::new()
    .select(&["id", "username", "email"])
    .from("users")
    .where_clause("email LIKE $1")
    .order_by("created_at DESC")
    .limit(10);

let users: Vec<User> = query.fetch_all(&client, &[&"%@example.com"]).await?;

// Insert with returning
let user: User = query!(
    client,
    "INSERT INTO users (username, email) VALUES ($1, $2) RETURNING *",
    "bob",
    "bob@example.com"
).fetch_one().await?;

Transactions

use entropydb::{Client, Transaction, IsolationLevel};

async fn transfer_funds(
    client: &Client,
    from_id: i64,
    to_id: i64,
    amount: f64,
) -> Result<(), Error> {
    // Begin transaction
    let mut tx = client.begin()
        .isolation_level(IsolationLevel::Serializable)
        .await?;
    
    // Debit from source
    tx.execute(
        "UPDATE accounts SET balance = balance - $1 WHERE id = $2",
        &[&amount, &from_id],
    ).await?;
    
    // Credit to destination
    tx.execute(
        "UPDATE accounts SET balance = balance + $1 WHERE id = $2",
        &[&amount, &to_id],
    ).await?;
    
    // Record transaction
    tx.execute(
        "INSERT INTO transfers (from_account, to_account, amount) VALUES ($1, $2, $3)",
        &[&from_id, &to_id, &amount],
    ).await?;
    
    // Commit transaction
    tx.commit().await?;
    
    Ok(())
}

// Savepoints
async fn with_savepoints(client: &Client) -> Result<(), Error> {
    let mut tx = client.begin().await?;
    
    tx.execute("INSERT INTO users (username) VALUES ('alice')", &[]).await?;
    
    // Create savepoint
    tx.savepoint("sp1").await?;
    
    tx.execute("INSERT INTO users (username) VALUES ('bob')", &[]).await?;
    
    // Rollback to savepoint
    tx.rollback_to("sp1").await?;
    
    // Only alice will be inserted
    tx.commit().await?;
    
    Ok(())
}

Connection Pooling

use entropydb::{Client, PoolConfig};
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pool_config = PoolConfig::builder()
        .max_size(20)
        .min_idle(5)
        .max_lifetime(Duration::from_secs(1800))
        .idle_timeout(Duration::from_secs(300))
        .connection_timeout(Duration::from_secs(10))
        .build();
    
    let client = Client::builder()
        .host("localhost")
        .port(5432)
        .database("mydb")
        .user("admin")
        .password("password")
        .pool_config(pool_config)
        .build()
        .await?;
    
    // Get pool stats
    let stats = client.pool_stats();
    println!("Active connections: {}", stats.active);
    println!("Idle connections: {}", stats.idle);
    println!("Total connections: {}", stats.total);
    
    Ok(())
}

Async Streams

use futures::stream::StreamExt;

async fn process_large_result() -> Result<(), Error> {
    let client = Client::connect("...").await?;
    
    // Stream results for memory efficiency
    let mut stream = client.query_stream(
        "SELECT * FROM large_table WHERE status = $1",
        &[&"active"],
    ).await?;
    
    // Process rows as they arrive
    while let Some(row) = stream.next().await {
        let row = row?;
        let id: i64 = row.get(0);
        // Process row without loading all into memory
        process_row(id).await?;
    }
    
    Ok(())
}

// Concurrent queries
use futures::future::join_all;

async fn concurrent_queries(client: &Client) -> Result<(), Error> {
    let queries = vec![
        client.query("SELECT COUNT(*) FROM users", &[]),
        client.query("SELECT COUNT(*) FROM orders", &[]),
        client.query("SELECT COUNT(*) FROM products", &[]),
    ];
    
    let results = join_all(queries).await;
    
    for result in results {
        let rows = result?;
        let count: i64 = rows[0].get(0);
        println!("Count: {}", count);
    }
    
    Ok(())
}

Best Practices

Safety

  • • Use prepared statements
  • • Leverage type system for validation
  • • Handle errors with Result types
  • • Use compile-time query checking

Performance

  • • Use connection pooling
  • • Stream large result sets
  • • Batch operations when possible
  • • Use zero-copy operations

Next Steps