Announcing the Official MongoDB Rust Driver

Sam Rossi

I’m delighted to announce that today we are releasing our first alpha build of the new official Rust driver for MongoDB.

In this blog post, I’ll cover:

  • The background on Rust at MongoDB and why we have built a new driver.
  • Show you how to get started with the driver.
  • Share how you can get involved with the project

Rust at MongoDB

MongoDB has a surprisingly long history with Rust. Back in 2013, two interns wrote a prototype Rust driver for the database targeting Rust 0.7, but because the Rust language was evolving quickly and making breaking changes at that time, the code eventually became out of date.

When I was an intern in the summer of 2015 I was part of a team writing a Rust driver prototype targeting Rust 1.0—which had been released less than two weeks before the internship started! We worked hard on the driver that summer with the knowledge that the code we wrote would continue working indefinitely on newer Rust compilers. At the end of the summer, we published our completed driver prototype onto crates.io under the crate name mongodb.

Almost four and a half years later, that driver still has an active user base; the crate has over 300,000 downloads, and Github issues and pull requests have shown us that users are eager for a driver that’s more than a prototype. We also see this enthusiasm reflected by the rise of Rust in developer communities. The most recent programming language rankings compiled by Steve O’Grady at Redmonk is a testament to this. To quote Steve’s commentary on Rust:

For a systems language to continue its upward trajectory in this fashion suggests that some combination of the design, the language’s community and market demand is combining to have it outperform its natural expectations.

Over the past several months, my team at MongoDB has been working on a new Rust driver written from scratch that we can commit to supporting at the same level as our other official drivers. I’m pleased to announce that we’re releasing the first alpha of an officially supported Rust driver for MongoDB—today!

Why Build a New Rust Driver?

Once we made the decision to build an officially supported Rust driver, we decided pretty quickly that writing a new driver from scratch, rather than updating the prototype, would be the best approach for us and the community. The prototype became quite popular for an unsupported driver and succeeded quite well as a proof of concept that a driver could be written in Rust. Monitoring MongoDB’s topology state in the background, selecting the server for each operation, managing a pool of connections for each server—every one of these components needs to be implemented efficiently in its own right and can’t block any of the other components from performing their tasks.

Furthermore, although we were starting work before Rust had stabilized async/await and would be targeting stable Rust for our initial release, we needed to minimize the difficulties of converting the drivers internals to async down the line, which would have been quite difficult without careful upfront design.

Supported Features Today

The Rust driver supports all MongoDB server versions from 3.6 up and requires Rust 1.39 or newer. It fully supports standalone instances, replica sets, and sharded clusters. It implements native TLS using the rustls crate, as well as SCRAM-SHA-1 and SCRAM-SHA-256 authentication.

The driver supports connecting to both MongoDB Atlas and on-premise deployments. Through the bson crate, the driver supports using serde to serialize and deserialize documents written to and from the database.

Y.T. Chung, the original author of the bson crate, has also agreed to transfer ownership of the crate to us, which will allow us to develop it alongside the driver. Thanks to Y.T. for all the excellent work on the crate over the years!

What Features Are Next?

Going forward, the next thing we plan to implement for the driver is async support; we’ve designed the driver architecture from the outset to convert it to async when ready. When that happens, the driver will continue providing the same sync API that it offers today by wrapping the async API.

After that, we’ll be implementing newer database features that aren’t yet supported by this alpha release—change streams, sessions, retryable reads and writes, multi-document ACID transactions, and more will all be coming!

Trying Out the Driver

The Rust driver can be imported to your client by adding the mongodb crate to your project’s Cargo.toml. The bson library is also needed for most usage of the driver.

mongodb = "0.9.0"
bson = "0.14.0"

To connect to the MongoDB deployment, create a Client using either a MongoDB connection string or through manually constructing a ClientOptions struct.

use mongodb::{Client, options::ClientOptions};

// Parse a connection string into an options struct.
let mut client_options =
    ClientOptions::parse("mongodb://localhost:27017")?;

// Manually set an option.
client_options.app_name = Some("My App".to_string());

// Get a handle to the deployment.
let client = Client::with_options(client_options)?;

// List the names of the databases in that deployment.
for db_name in client.list_database_names(None)? {
    println!("{}", db_name);
}

In addition to a few operations being defined directly on it, a Client also has methods to obtain a Database and perform database-specific operations.

// Get a handle to a database.
let db = client.database("mydb");

// List the names of the collections in that database.
for collection_name in db.list_collection_names(None)? {
    println!("{}", collection_name);
}

Similarly, a Collection can be obtained from a Database. All the MongoDB CRUD operations you might be familiar with are defined on Collection.

use bson::{doc, bson};

// Get a handle to a collection in the database.
let collection = db.collection("books");

let docs = vec![
    doc! { "title": "1984", "author": "George Orwell" },
    doc! { "title": "Animal Farm", "author": "George Orwell" },
    doc! { "title": "The Great Gatsby", "author": "F. Scott Fitzgerald" },
];

// Insert some documents into the "mydb.books" collection.
collection.insert_many(docs, None)?;

Collection::find returns a Cursor, which implements Rust’s Iterator trait.

use bson::{doc, bson};
use mongodb::options::FindOptions;

// Query the documents in the collection with a filter and an option.
let filter = doc! { "author": "George Orwell" };
let find_options = FindOptions::builder()
    .sort(doc! { "title": 1 })
    .build();
let cursor = collection.find(filter, find_options)?;

// Iterate over the results of the cursor.
for result in cursor {
    match result {
        Ok(document) => {
            if let Some(title) = document.get("title").and_then(Bson::as_str) {
                println!("title: {}", title);
            }  else {
                println!("no title found");
            }
        }
        Err(e) => return Err(e.into()),
    }
}

If you want to try something a little more demanding than connecting to local host, then spin up MongoDB on our fully-managed Atlas cloud service. The documentation steps you through how to create a free MongoDB database cluster in the region and on the leading cloud provider of your choice. It includes steps on how to load our pre-prepared sample datasets, so you have a simple way of running queries from your Rust apps.

How You Can Get Involved

We hope that you in the community are as excited about this release as we are! If you try out the driver and find any bugs or have any feature requests, please file a ticket in our JIRA project here. We also will happily accept contributions in the form of pull requests; see the section in our README for info on how to run our tests.

We hope you enjoy using the driver, and please do let us know what you think!

Safe Harbor

The development, release, and timing of any features or functionality described for our products remains at our sole discretion. This information is merely intended to outline our general product direction and it should not be relied on in making a purchasing decision nor is this a commitment, promise or legal obligation to deliver any material, code, or functionality.