←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