Docs Menu
Docs Home
/ / /
Rust Driver
/

Asynchronous and Synchronous APIs

On this page

  • Overview
  • Configure the Asynchronous Runtime
  • Tokio Runtime Example
  • Configure the Synchronous API
  • Synchronous Code Example
  • Use Both Asynchronous and Synchronous APIs
  • Additional Information
  • API Documentation

In this guide, you can learn about the Rust driver's asynchronous and synchronous APIs. This guide explains how to enable the available APIs and structure your code to use each.

The Rust driver supports the tokio asynchronous runtime crate, which is the default runtime.

The driver also includes a synchronous API for use cases that require blocking, or when parallelism is not necessary. You can select the synchronous API by adding feature flags to the mongodb dependency in your Cargo.toml file.

This guide includes the following sections:

The driver uses the tokio runtime by default, so you can use this runtime without specifying any feature flags in your project's Cargo.toml file. For more information on installing the driver and configuring your Cargo.toml file, see the Download and Install step of the Quick Start.

Important

Beginning in Rust driver v3.0, the tokio runtime is the only asynchronous runtime crate that the driver supports. Previous versions of the driver also support the async-std runtime crate.

If your application uses the async-std runtime, you can start a tokio runtime in the same application by creating a Runtime struct instance and wrapping driver operations with the Runtime::spawn() method. Ensure that you use the same Runtime instance for instantiating a Client and calling driver methods on that Client instance.

For an example that instantiates a Runtime and calls the spawn() method, see the tokio documentation.

The following code uses the task module from the tokio crate to create separate, concurrent tasks for multiple data operations:

let client = Client::with_uri_str("<connection string>").await?;
let some_data = doc! { "title": "1984", "author": "George Orwell" };
for i in 0..5 {
let client_ref = client.clone();
let somedata_ref = some_data.clone();
task::spawn(async move {
let collection = client_ref
.database("items")
.collection::<Document>(&format!("coll{}", i));
collection.insert_one(somedata_ref).await
});
}

The driver also provides a blocking, synchronous API. To use the tokio synchronous API, add the "sync" feature flag to the mongodb dependency, as shown in the following example:

[dependencies.mongodb]
version = "3.1.0"
features = ["sync"]

When using the synchronous API, use types from the mongodb::sync module to perform operations. The following code uses the sync module to insert data into a collection using the synchronous API. When the insert_one method runs inside the for loop, the driver waits for each request to complete before continuing.

use mongodb::sync::Client;
fn main() {
let client = Client::with_uri_str("<connection string>")?;
let some_data = doc! { "title": "1984", "author": "George Orwell" };
for i in 0..5 {
let client_ref = client.clone();
let somedata_ref = some_data.clone();
let collection = client_ref
.database("items")
.collection::<Document>(&format!("coll{}", i));
collection.insert_one(somedata_ref);
}
}

You can use both asynchronous and synchronous APIs in the same application. For example, to enable both tokio runtimes, you can add the tokio dependency to your dependencies list and add the "sync" flag to the mongodb dependency:

[dependencies]
futures = "0.3.28"
tokio = {version = "1.32.0", features = ["full"]}
[dependencies.mongodb]
version = "3.1.0"
features = ["sync"]

For more information about the concepts in this guide, see the following pages:

To learn more about the methods and types mentioned in this guide, see the following API documentation:

Back

Performance Considerations