Rust data structure example

I’m reading the documentation for this, now v1.0.0, official mongodb Rust driver.

While it shows how to insert some arbitrary data

let result = coll.insert_one(doc! { "x": 1 }, None).await?;

I’m wondering how to define data structures.

Can you show an example data structure definiton?

For instance something like

pub struct Post {
  id: bson::ObjectID
  name: String
}

only correct.

Is this question too trivial?

Hi @dalu!

Your question is not too trivial at all! In fact, it touches on one of the nicest things about using MongoDB in Rust, which is that converting between BSON and your Rust types can be done seamlessly using serde.

For your specific example, you’ll need to derive the Serialize trait on your struct. Then, you can use mongodb::bson::to_bson to encode it to BSON for insertion.

Complete example:

use mongodb::bson;
use serde::Serialize;

#[derive(Serialize)]
struct Post {
    #[serde(rename = "_id")]
    id: bson::oid::ObjectId,
    name: String,
}

let post = Post {
    id: bson::oid::ObjectId::new(),
    name: "Bill".to_string(),
};

let serialized_post = bson::to_bson(&post).unwrap();
let result = coll
    .insert_one(bson::from_bson(serialized_post).unwrap(), None)
    .await
    .unwrap();

For more examples of working with BSON in Rust, check out the documentation for the bson crate: bson - Rust

2 Likes

Hello Patrick,

thank you for your response.
Would it be too much to ask for a complete CRUD example with actix-web?

I’m pretty new to Rust and I’m finding it very unintuitive, having a hard time with learning the language, despite this being my 4th programming language I’m getting into.

I’ll post the files I have and the cargo.toml

main.rs

mod model;
mod handler;
use mongodb::Client;

use std::io;
use actix_web::{HttpServer, App, web};

pub struct State {
    client: mongodb::Client
}

#[actix_rt::main]
async fn main() -> io::Result<()> {
    let client = Client::with_uri_str("mongodb://localhost:27017/").await.expect("mongo error");

    HttpServer::new(move || {
        App::new()
            .data(State{client: client.clone()})
            .route("/", web::get().to(handler::index))
    })
        .bind("127.0.0.1:8080")?
        .run()
        .await
}

model.rs

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
pub struct Blog {
    #[serde(rename = "_id")]
    pub id: bson::oid::ObjectId,
    pub name: String,
    pub description: String,
}

#[derive(Serialize, Deserialize)]
pub struct Post {
    #[serde(rename = "_id")]
    pub id: bson::oid::ObjectId,
    pub slug: String,
    pub author: String,
    pub title: String,
    pub body: String,
}

#[derive(Serialize, Deserialize)]
pub struct Comment {
    #[serde(rename = "_id")]
    pub id: bson::oid::ObjectId,
    pub author: String,
    pub body: String,
}

handler.rs

use actix_web::{web, HttpRequest, Responder, HttpResponse};
use crate::State;
use crate::model::Blog;

pub async fn index(_data: web::Data<State>, req: HttpRequest) -> impl Responder {
    let coll = _data.client.database("mblog").collection("blogs");
    let cursor = coll.find(None, None);
    let mut m: Vec<Blog> = Vec::new();

    for result in cursor {
        if let Ok(item) = result {
            m.push(item)
        }
    }

    HttpResponse::Ok().json(m)
}
[package]
name = "mblog"
version = "0.1.0"
authors = [""]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
mongodb = "1.0.0"
actix-web = "2"
actix-rt = "1.1.1"
serde = "1.0.114"
bson = "1.0.0"

The following error occurs

error[E0277]: `impl std::future::Future` is not an iterator
  --> src/handler.rs:10:19
   |
10 |     for result in cursor {
   |                   ^^^^^^ `impl std::future::Future` is not an iterator
   |
   = help: the trait `std::iter::Iterator` is not implemented for `impl std::future::Future`
   = note: required by `std::iter::IntoIterator::into_iter`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
error: could not compile `mblog`.

I have changed the handler.rs index function to look like this:

use actix_web::{web, HttpRequest, Responder, HttpResponse};
use crate::State;
use crate::model::Blog;

pub async fn index(_data: web::Data<State>, req: HttpRequest) -> impl Responder {
    let coll = _data.client.database("mblog").collection("blogs");
    let cursor = coll.find(None, None).await?;
    let mut m: Vec<Blog> = Vec::new();

    while let Some(result) = cursor.next().await {
        match result {
            Ok(item) => {
                m.push(item)
            }
            Err(e) => return Err(e.into()),
        }
    }

    HttpResponse::Ok().json(m).await
}

but I receive the following error:

error[E0599]: no method named `next` found for struct `mongodb::cursor::Cursor` in the current scope
  --> src/handler.rs:10:37
   |
10 |     while let Some(result) = cursor.next().await {
   |                                     ^^^^ method not found in `mongodb::cursor::Cursor`
   |
   = help: items from traits can only be used if the trait is in scope
   = note: the following traits are implemented but not in scope; perhaps add a `use` for one of them:
           candidate #1: `use std::iter::Iterator;`
           candidate #2: `use std::str::pattern::Searcher;`
           candidate #3: `use tokio::stream::StreamExt;`
           candidate #4: `use futures_util::stream::stream::StreamExt;`
           candidate #5: `use serde_json::read::Read;`

error[E0277]: the trait bound `mongodb::error::Error: actix_http::error::ResponseError` is not satisfied
 --> src/handler.rs:7:45
  |
7 |     let cursor = coll.find(None, None).await?;
  |                                             ^ the trait `actix_http::error::ResponseError` is not implemented for `mongodb::error::Error`
  |
  = note: required because of the requirements on the impl of `std::convert::From<mongodb::error::Error>` for `actix_http::error::Error`
  = note: required by `std::convert::From::from`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `mblog`.
2 Likes

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.