Announcing our Rust Driver: Version 1.0
I’m happy to announce that today we are releasing version 1.0.0 of the official Rust driver for MongoDB. This release marks the driver as generally available and ready for production usage. It supports MongoDB server versions 3.6 and later. In this blog post, I’ll cover:
- What’s changed in the driver since the initial alpha release
- How to get started with the driver.
- How you can get involved with the project
Where to find the driver
- GitHub: https://github.com/mongodb/mongo-rust-driver
- crates.io: https://crates.io/crates/mongodb
- docs.rs: https://docs.rs/mongodb
- MongoDB docs: https://docs.mongodb.com/drivers/rust
The Journey to 1.0
We released the first alpha of the official MongoDB Rust Driver back in December 2019, and our first beta of the driver came out a little over a month ago. The key feature of the GA release is the stabilization of the driver's API. This includes the async and sync database APIs in the driver itself as well as the BSON API that the driver re-exports from the Rust bson library. Going forward, updating to new releases of the driver will require no code changes to user applications.
In addition, this release marks a transition from the Rust driver team’s focus on the core architecture to achieving feature parity with the rest of the MongoDB driver ecosystem. Moving forward, we’ll be working on exciting new features such as multi-document ACID transactions support and retryable writes.
Async support
After the Rust language stabilized async/await back in November, async usage in the Rust community skyrocketed. Therefore, our first priority after the initial alpha release was compatibility with the growing async Rust ecosystem. We spent six months designing and reimplementing the driver from the ground up to be fully async, and we released the first async version of the driver with our 0.10.0 beta in early May. Our GA release contains out-of-the-box support for the two most popular async runtimes in the Rust ecosystem, tokio and async-std. In addition, we provide a sync wrapper of the driver that abstracts all async details of the inner API for developers who are not writing async applications.
Trying out the driver
To get started with the driver, first create a new Rust project following the steps outlined here. Next, add the mongodb
crate to your Cargo.toml
. By default, the driver will use tokio as the underlying async runtime.
[dependencies]
mongodb = "1.0.0"

The driver can then be imported and used by your project:
use mongodb::{
 bson::doc,
 error::Result,
 Client
};

#[tokio::main]
async fn main() -> Result<()> {
 // Create the client by passing in a MongoDB connection string.
 //
 // When connecting to Atlas, the connection string prefixed with
 // "mongodb+srv" can be used in this manner.
 let client =
 Client::with_uri_str("mongodb://localhost:27017").await?;

 // Get a handle to the collection being used.
 let coll = client
 .database("animals")
 .collection("pets");

 // Create a vector of documents to insert into the database by 
 // using the `doc` macro, which allows creation of BSON documents 
 // using JSON-like syntax.
 let new_pets = vec![
 doc! { "type": "dog", "name": "Rondo" },
 doc! { "type": "cat", "name": "Smokey" },
 doc! { "type": "cat", "name": "Phil" }, 
 ];

 // Insert the document into the database.
 coll.insert_many(new_pets, None).await?;

 Ok(())
} 

In addition to using the doc
macro, the driver provides the ability to leverage serde to serialize and deserialize native Rust types into BSON. To use this, first add serde
to your project’s Cargo.toml
with the derive
feature enabled:
[dependencies.serde]
version = "1.0.111"
features = ["derive"]

Then in your code, derive the Serialize
and Deserialize
traits on your types that you wish to read and write to the database:
use mongodb::{
 bson::{doc, Bson},
 error::Result,
 Client
};

// Import the `Deserialize` and `Serialize` traits from serde.
use serde::{Deserialize, Serialize};

// Derive `Deserialize` and `Serialize` on your custom type. 
#[derive(Deserialize, Serialize)]
struct Pet {
 // "type" is a reserved Rust keyword, so instead of naming our 
 // struct field that, we use serde’s `rename` functionality to 
 // specify it as the name to use when serializing and 
 // deserializing.
 #[serde(rename = "type")]
 pet_type: String,
 name: String,
}

#[tokio::main]
async fn main() -> Result<()> {
 // Create the client by passing in a MongoDB connection string.
 //
 // When connecting to Atlas, the connection string prefixed with
 // "mongodb+srv" can be used in this manner.
 let client =
 Client::with_uri_str("mongodb://localhost:27017").await?;

 // Get a handle to the collection being used.
 let coll = client
 .database("animals")
 .collection("pets");
 
 // Query the database for all pets which are cats.
 let mut cursor = coll.find(doc! { "type": "cat" }, None).await?;

 // Iterate over each document in the cursor, using serde to 
 // deserialize them into Pets.
 while let Some(doc) = cursor.next().await? {
 let pet: Pet = bson::from_bson(Bson::Document(doc))?;
 println!("There is a {} named {}", pet.pet_type, pet.name);
 }

 Ok(())
} 

Running the first example followed by the second will print the following:
There is a cat named Smokey
There is a cat named Phil

To use async-std instead of tokio, disable the default features of the library and specify the async-std-runtime
feature:
[dependencies.mongodb]
version = "1.0.0"
default-features = false
features = ["async-std-runtime"]

Using the sync API
To use the sync API instead of async, disable the default features of the library and specify the "sync" feature:
[dependencies.mongodb]
version = "1.0.0"
default-features = false
features = ["sync"]

use mongodb::{
 bson::doc,
 error::Result,
 
 // Note that the sync version of the Client is in the `sync`
 // module rather than at the top-level of the driver’s namespace. 
 sync::Client
};

fn main() -> Result<()> {
 // Create the client by passing in a MongoDB connection string.
 //
 // When connecting to Atlas, the connection string prefixed with
 // "mongodb+srv" can be used in this manner.
 let client =
 Client::with_uri_str("mongodb://localhost:27017")?;

 // Get a handle to the collection being used.
 let coll = client
 .database("animals")
 .collection("pets");

 // Create a vector of documents to insert into the database by 
 // using the `doc` macro, which allows creation of BSON documents 
 // using JSON-like syntax.
 let new_pets = vec![
 doc! { "type": "dog", "name": "Rondo" },
 doc! { "type": "cat", "name": "Smokey" },
 doc! { "type": "cat", "name": "Phil" }, 
 ];

 // Insert the document into the database.
 coll.insert_many(new_pets, None)?;

 Ok(())
}

How You Can Get Involved
We are excited for you to try out the driver! If you find any bugs or have any feature requests, please file a ticket in our JIRA project here. We will also happily accept contributions in the form of Github pull requests - please see the section in our README for info on how to run our tests.
We hope you enjoy using the driver; let us know what you think! You can contact us by making a post on the MongoDB community forum.