Today, we are releasing version 0.1.0 of the experimental MongoDB driver for the Rust programming language. This is the spiritual successor to the driver for Rust 0.7, built two years ago by previous interns Jao-ke Chin-Lee and Jed Estep. In those two years, Rust has changed dramatically to become a language that is faster, safer, more concurrent, and more usable for developers. We’re excited to be releasing our driver into the wilderness, and look forward to feedback from both the Rust and MongoDB communities.
Rust is a new systems-oriented programming language currently in development at Mozilla. Officially, it is described as a language that “runs blazingly fast, prevents nearly all segfaults, and guarantees thread safety.” Its powerful type- and memory-safety features run at compile time, eliminating common missteps like dangling pointers and data races at runtime while maintaining performance typical of a low-level language.
In addition, it features elements from imperative, functional, and object-oriented programming languages, providing high-level abstractions like trait-based generics, pattern matching, type inferencing, and automatic memory management at little to no runtime cost. As the language continues to grow, the availability of a MongoDB driver written in native Rust exposes both Rust and MongoDB to new audiences.
The New MongoDB Rust Driver
The driver is an open-source, Apache-licensed library implemented entirely in Rust. It will feel familiar to users of the current generation of MongoDB drivers, which have been rebuilt to conform to a growing collection of published specifications, guiding everything from the user-facing API to the internal mechanics of server monitoring and selection. The driver is available on crates.io as mongodb.
To use the driver as a library dependency, add the bson and mongodb crates to your
Afterwards, you can import the crates within your code and use them freely.
Connecting to MongoDB
To connect to a MongoDB server, we need to create a client. We can connect directly to a server on localhost:27017 like so:
We can also connect to a more complex topology, such as a replica set or sharded cluster:
Connecting with a connection string will trigger automatic monitoring of your servers, allowing the driver to discover other servers within your server set and to react to changes within your deployment topology.
More complex options are available through the
ClientOptions struct, allowing you to specify log files for command monitoring, read preferences and write concerns for complex topologies, and other configuration options like server timeouts and heartbeat monitoring intervals.
Now that we’ve connected to our servers, let’s create a new capped collection within the ‘movies’ database. We can easily do this through the database API:
If the database is protected using SCRAM-SHA-1 authentication measures, we can authorize ourselves through the driver before executing our operations:
At this time other authentication measures are unimplemented. We look forward to supporting the full auth specification in the future.
Now we can interact with our new collection. For those that have used other drivers within the MongoDB ecosystem, interacting with your data in Rust will feel very familiar. We can do things like insert multiple documents into our collection:
Along with the basic CRUD operations, the driver provides a bulk write API for efficiently executing multiple write operations at once.
The return type of a query from the database is
Result<Cursor>. This means that if the query completed successfully, a
Cursor will be returned, and if not, an
Error will be returned. The most common way to check whether the query was successful is to pattern match on the result:
Cursor class implements the
Iterator trait. The iteration type is
Result<bson::Document>. You can iterate over the documents by using a “for” loop:
You can also use any of the
Iterator methods on the cursor:
Tariff -- An Import/Export App
To test our driver, we wrote a few applications using it. One of these,
tariff, serves as a good example of some basic driver usage.
tariff is an import/export tool for MongoDB databases. It has the ability to dump either single collections or entire databases into a JSON file and to import the files generated by an export. By default,
tariff will read from whatever database it connects to, but it also has the ability to read from a secondary in a replica set rather than a primary.
tariff, clone the github repository and then build the app using
After following the above steps, the
tariff binary will be located in
target/release/tariff. To see a list of available arguments, you can run
Note: all of the below examples assume that they’re being run from the same directory as the
tariff binary. You may need to prefix the name with the path to it; for example, to run from the project root, you’ll need to use
- Exporting the collection “players” in the “baseball” database to the file “players.json”:
tariff -e players.json -d baseball -c players
- Exporting the entire “baseball” database to the file “baseball.json”:
tariff -x baseball.json -d baseball
- Importing the collection stored in “players.json” into the collection “baseball” in the database “athletes”:
tariff -i players.json -d athletes -c baseball
- Importing all collections stored in “baseball.json” into the database "favorite
tariff -m baseball.json -d favorite_
- Exporting a collection from one of the secondaries in a replica set rather than the primary:
tariff -e players.json -d mlb -c players --secondary
- Importing a collection into a database running on port 3000:
tariff -i players.json -d atheletes -b baseball -p 3000
To see how
tariff uses the Rust driver, you can look at the file src/client.rs.
To learn more about the driver, visit the github repositories for both the bson library and the driver itself. Documentation is available on github pages. We highly encourage you to play around with the driver and to provide feedback in the form of issues and pull requests!
To learn more about Rust in general, check out our slides for the Rust NYC meetup hosted at MongoDB HQ. Below is an enumeration of online resources that we have used and recommended to become more familiar with Rust and to claim a deeper understanding of the internal workings of the language:
- The Rust Book
- Wrapper Types in Rust: Choosing Your Guarantees
- Error Handling in Rust
- Some Notes on Send and Sync
- A Practical Intro to Macros in Rust 1.0
- Rust by Example
Thank you to the Rust community for providing all of the tools and resources that make it easy to pick up and become familiar with the language, and to all of the contributors we’ve interacted with that have made our lives easier. A final thank you to all of the wonderful people at MongoDB that make the internship program special, especially our mentors, Daniel Alabi and Valeri Karpov, for their guidance and support.
Note: Originally appeared on the MongoDB blog written by Sam Rossi and Kevin Yeh.
About the Authors
Sam Rossi is a rising senior at the University of Pennsylvania majoring in computer science. He enjoys doing systems programming, writing compilers and interpreters, and solving interesting problems in general.
Kevin Yeh is a rising Master's student at the University of Texas at Austin, and was an intern on the Drivers team at MongoDB this summer.