Docs Menu

Docs HomeDevelop ApplicationsMongoDB DriversRust Driver

FAQ

On this page

  • Why Do I Get Errors While Connecting to MongoDB?
  • How Does Connection Pooling Work in the Rust Driver?
  • How Do I Convert Between BSON and Rust Types?
  • How Do I Fix Unsatisfied Trait Bounds Errors?
  • How Do I Process a Value Wrapped in a Result or Option Enum?

On this page, you can find frequently asked questions and their corresponding answers.

Tip

If you can't find an answer to your question on this page, see the Issues & Help page for information on how to report issues.

If you have trouble connecting to a MongoDB deployment, see the Connection Troubleshooting guide for possible solutions.

Each server in your MongoDB cluster maintains a connection pool. You can access or manage the behavior of the connection pool by using an instance of a Client. Connection pools open sockets on demand to support concurrent operations in your multi-threaded application.

You can configure the following connection pool features:

  • Maximum and minimum size, set by the max_pool_size and min_pool_size options

  • Maximum number of connections that the pool creates in parallel, set by the max_connecting option

  • Maximum idle time, set by the max_idle_time option

For more information about connection pooling, see the Connection Pool section of the Performance Considerations guide.

The Rust driver and the BSON library use the Serde framework to perform conversions between custom Rust types and BSON. You can add the serde crate to your Cargo.toml file to access the functionality of the Serde framework. For instructions on adding this crate, see serde in the crates registry.

After you add the crate to your application, you can model the documents in a collection by using a custom type instead of a BSON document. The following example includes the derive attribute before the Vegetable struct definition, which instructs the driver to perform the following actions when needed:

  • Serialize the struct, which converts the struct to BSON

  • Deserialize BSON, which converts BSON data to your struct

#[derive(Serialize, Deserialize)]
struct Vegetable {
// Add struct fields here
}

You can then create a Collection instance with your custom struct type as its generic type parameter. The following example assigns a Collection instance parameterized with the Vegetable type to the my_coll variable:

let my_coll: Collection<Vegetable> = client.database("db").collection("vegetables");

For more information about converting between BSON and Rust types, see the Data Modeling and Serialization guide and the Structuring Data with Serde in Rust MongoDB Developer Center article.

Trait bounds allow methods to restrict which types they accept as parameters and what functionality those types must implement. For example, if you define a method that accepts a generic type parameter and prints its value, the parameter must implement the Display trait for printing purposes. The following example defines the printer() method and specifies that its parameter must implement Display:

fn printer<T: Display>(t: T) {
println!("{}", t);
}

When calling a method on a data type, you might encounter an error stating that the method's trait bounds are not satisfied. For example, the driver might raise the following error message when you call the try_next() method on a Cursor instance:

error[E0599]: the method `try_next` exists for struct `mongodb::Cursor<T>`,
but its trait bounds were not satisfied

The Cursor<T> type only implements the Stream trait, which is required to access the try_next() method, if the trait bounds for T are satisfied. Namely, T must implement the DeserializeOwned trait, as specified in the Cursor API documentation. The following example replicates the preceding error message by defining a custom Actor struct to model documents in the actors collection. However, this struct does not implement the DeserializeOwned trait, and using the try_next() method to iterate over Actor instances causes an error:

struct Actor {
name: String,
}
// Add setup code here
let my_coll: Collection<Actor> = client.database("db").collection("actors");
let mut cursor = my_coll.find(None, None).await?;
while let Some(result) = cursor.try_next().await? {
println!("{:?}", result);
};

To resolve the trait bounds error, identify the data type over which the cursor iterates and ensure that this data type implements the DeserializeOwned trait. You can use the derive attribute to apply the required trait bound.

Note

Deserialize and DeserializeOwned Traits

The serde crate provides derive macros to generate the implementation of certain traits, including the Deserialize trait. However, this crate does not offer a derive macro for the DeserializeOwned trait. Data types that implement Deserialize without lifetime restrictions automatically implement DeserializeOwned, so you can implement Deserialize to fulfill the DeserializeOwned trait bound.

The following example adjusts the Actor struct definition to implement Deserialize:

#[derive(Deserialize)]
struct Actor {
name: String,
}

For more information about trait bounds, see the following resources:

Rust provides the Result and Option enums as safeguards for your application code. Many methods offered by the Rust driver return values wrapped in one of these two types.

The Result enum can return the following variants:

  • Ok(T): wraps the value of the result of the operation

  • Err(E): wraps an error value if the operation is unsuccessful

For example, the insert_one() method returns a Result type to wrap either a successful response or an error.

To access the unwrapped result of insert_one(), use the ? operator. If the operation is successful, the method returns the Ok(InsertOneResult) variant of the Result enum. In this case, the ? operator unwraps the InsertOneResult value and assigns it to the insert_one_result variable. If the operation is unsuccessful, the method returns the Err(E) enum variant, and the ? operator unwraps and returns the error value. The following code demonstrates the syntax for using the ? operator while handling an insert operation result:

let insert_one_result = my_coll.insert_one(doc, None).await?;

Alternatively, you can create a conditional to handle the unwrapped values of InsertOneResult. The following code uses the match keyword to process the insert_one() result:

let insert_one_result = my_coll.insert_one(doc, None).await;
match insert_one_result {
Ok(val) => {
println!("Document inserted with ID: {}", val.inserted_id);
},
Err(err) => {
println!("Operation not successful");
}
}

The Option enum can return the following variants:

  • None: represents an empty value returned by an operation

  • Some(T): wraps a non-empty return value

Some Rust driver methods return an Option type, such as the read_concern() method. This method returns an Option that wraps either an empty value, if no read concern exists, or a ReadConcern value.

To access the result of read_concern(), you can use the same match syntax as shown in the preceding example to process the None and Some(T) variants. Alternatively, you can use the if let syntax to process only the Some(T) variant. The following code unwraps and prints the non-empty read_concern() return value, if it exists:

if let Some(rc) = my_coll.read_concern() {
println!("Read concern: {:?}", rc);
}

For more information about the Result and Option enums, see the following resources in the Rust language documentation:

←  GridFSConnection Troubleshooting →