Search code examples
mongodbrustrust-cargorust-tokiorust-axum

'Bson: Borrow<T> is not satisifed' error in simple CRUD post function


I have following Error:

error[E0277]: the trait bound `Bson: Borrow<News>` is not satisfied                                                                                                                               
    --> src\handlers.rs:46:36
     |
46   |     let inserted = coll.insert_one(serialized_news, None).await.unwrap();
     |                         ---------- ^^^^^^^^^^^^^^^ the trait `Borrow<News>` is not implemented for `Bson`
     |                         |
     |                         required by a bound introduced by this call
     |
note: required by a bound in `mongodb::Collection::<T>::insert_one`
    --> C:\Users\User\.cargo\registry\src\github.com-1ecc6299db9ec823\mongodb-2.3.1\src\coll\mod.rs:1279:19
     |
1279 |         doc: impl Borrow<T>,
     |                   ^^^^^^^^^ required by this bound in `mongodb::Collection::<T>::insert_one`

For more information about this error, try `rustc --explain E0277`.  
error: could not compile `backend` due to previous error

My handler code containing the post_news function that throws this error:

use crate::structs::News;
use axum::{extract::Path, extract::State, http::StatusCode, response::IntoResponse, Json};
use bson::oid::ObjectId;
use futures::stream::StreamExt;
use mongodb::{bson::doc, options::FindOptions, Client, Collection};

pub async fn get_all(State(client): State<Client>) -> impl IntoResponse {
    let coll: Collection<News> = client.database("axum").collection::<News>("news");

    let mut options = FindOptions::default();
    options.limit = Some(1);
    options.sort = Some(doc! {
        "title": 1
    });

    let mut cursor = coll
        .find(None, options)
        .await
        .expect("could not load news data.");

    let mut rows: Vec<News> = Vec::new();

    while let Some(doc) = cursor.next().await {
        rows.push(doc.expect("could not load news info."));
    }

    (StatusCode::OK, Json(rows))
}

pub async fn get_one(Path(id): Path<u64>) {}

pub async fn post_news(
    State(client): State<Client>,
    Json(payload): Json<News>,
) -> impl IntoResponse {
    let coll: Collection<News> = client.database("axum").collection::<News>("news");

    let news = News {
        id: ObjectId::new(),
        title: payload.title.to_string(),
        short_description: payload.short_description.to_string(),
    };

    let serialized_news = bson::to_bson(&news).unwrap();

    let inserted = coll.insert_one(serialized_news, None).await.unwrap();

    (StatusCode::CREATED, Json(news))
}

pub async fn handler_404() -> impl IntoResponse {
    (StatusCode::NOT_FOUND, "nothing to see here")
}

mod structs {
    use bson::{self, oid::ObjectId};
    use serde::{Deserialize, Serialize};

    #[derive(Debug, Serialize, Deserialize)]
    #[serde(rename_all = "snake_case")]
    pub struct News {
        #[serde(rename = "_id")]
        pub id: ObjectId,
        pub title: String,
        pub short_description: String,
    }
}

The error occurs in this line in the post_news function:

let inserted = coll.insert_one(serialized_news, None).await.unwrap();

Tried to add Clone and Debug to my struct and that didn't help


Solution

  • When encountering an error, you need to dig in and figure out why the error occurs, rather than just fiddling left and right.

    In this case, the first step is looking at the function you are attempting to call Collection::insert_one:

    pub async fn insert_one(
        &self,
        doc: impl Borrow<T>,
        options: impl Into<Option<InsertOneOptions>>
    ) -> Result<InsertOneResult>
    

    And double checking what Borrow is by following the link -- just to make sure it's indeed std::borrow::Borrow (spoiler: it is).

    So, since your coll is of type Collection<News>, you need a type implementing Borrow<News> -- as mentioned by the error message -- which means amongst others News and &News.

    Hence, as shown in the documentation of Collection, you should just insert your News object, without first serializing it:

    let inserted = coll.insert_one(news, None).await.unwrap();
    

    Collection will take care of serializing and deserializing as appropriate, no need to do the bson step yourself.