Skip to main content

Getting started

Your first program

This program builds an operator, writes a file, reads it back, inspects its metadata, and deletes it. It runs against the in-memory service with no credentials, so you can paste it into a fresh project and run it as-is.

# Cargo.toml
[dependencies]
opendal = "0.57"
tokio = { version = "1", features = ["full"] }
use opendal::Operator;
use opendal::Result;
use opendal::services;

#[tokio::main]
async fn main() -> Result<()> {
// Configure a service, then build an operator from it.
let op = Operator::new(services::Memory::default())?;

// The same verbs work on every service.
op.write("hello.txt", "Hello, World!").await?;

let bytes = op.read("hello.txt").await?;
println!("read {} bytes", bytes.len());

let meta = op.stat("hello.txt").await?;
println!("size = {} bytes", meta.content_length());

op.delete("hello.txt").await?;
Ok(())
}

Point it at a real backend

Only the service changes; the operations stay identical. To use S3, enable its feature and configure the builder:

opendal = { version = "0.57", features = ["services-s3"] }
use opendal::services;
use opendal::Operator;

let op = Operator::new(
services::S3::default()
.bucket("my-bucket")
.region("us-east-1"),
)?;

op.write("hello.txt", "Hello from S3!").await?;

Each service is gated behind its own services-* feature, so enable the ones you use. The next page, Connecting to your storage, covers the construction options and credentials in depth; Services lists every backend and its configuration keys.

Blocking vs async

OpenDAL is async-first. Operator exposes async methods and needs an async runtime (Tokio by default).

For synchronous code, enable the blocking feature and use blocking::Operator. Build it from an async operator, then call the same verbs without .await:

opendal = { version = "0.57", features = ["services-memory", "blocking"] }
use opendal::blocking;
use opendal::services;
use opendal::Operator;

// Build the async operator first, outside any async runtime.
let op = Operator::new(services::Memory::default())?;
let bop = blocking::Operator::new(op)?;

bop.write("hello.txt", "Hello, World!")?;
let bytes = bop.read("hello.txt")?;

The blocking operator drives the async one on a managed runtime, so construct it outside of an existing async context.