Saving Nested Struct with Rust mongodb returns error the trait `From<T>` is not implemented for `Bson`

I have a struct to model an Item. But some of its field depends of other struct. And I want to save this nested object into mongodb with MongoDB Rust Driver. (GitHub - mongodb/mongo-rust-driver: The official MongoDB Rust Driver)

     use mongodb::bson::doc;
     use serde::{Deserialize, Serialize};

     #[derive(Serialize, Deserialize, Debug)]
     struct CustomUnit {
         pub unit: String,
         pub multiplier: f64,
     }

     // Item depends on CustomUnit struct above. Nested object, JSON-like
     struct Item {
        pub name: String,
        pub qty: f64,
        pub unit: CustomUnit ,
     }

     // declare an item and an unit
     let my_unit = CustomUnit {unit: "BOX".to_string(), multiplier: 12.0};
     let a = Item {name: "FOO Item".to_string(), qty: 10.0, unit: my_unit};

     // later in the code I extracted the value for each field
     let name = a.name.clone();
     let qty = a.qty;
     let unit = a.unit;

     let doc = doc! {
        "name": name.clone(),
        "qty": qty,
        "unit": base_unit,
    };

    // throws an error: "the trait `From<CustomUnit>` is not implemented for `Bson`"
    db.collection(COLL).insert_one(doc, None).await

this displays an error message:

_^ the trait `From<CustomUnit>` is not implemented for `Bson`
= help: the following implementations were found:
         <Bson as From<&T>>
         <Bson as From<&[T]>>
         <Bson as From<&str>>
         <Bson as From<Regex>>
       and 19 others
= note: required by `std::convert::From::from`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)

How to implement this From<CustomUnit> for Bson trait ?

impl From<CustomUnit> for Bson {
    fn from(unit: CustomUnit) -> Self {
       // what to do here ? and how to return a Bson document ?
       // there's no explanation in rust-mongodb manual
    }
}

This was actually a bit tricky. The documentation section on Creating Bson Instances states that " bson! has supports both array and object literals, and it automatically converts any values specified to Bson , provided they are Into<Bson>." When I tried implementing Into<Bson> and passing unit.into() however, it failed to compile, stating that type annotations were needed.

So essentially there's two ways I found to do this: either build a Bson::Document inline, or implement Into<Bson> for CustomUnit, then use an explicit trait signature to convert the unit variable.

Building the Bson::Document inline:

let doc = doc! {
    "name": name.clone(),
    "qty": qty,
    "unit": Bson::Document(doc! {
        "unit": unit.unit,
        "multiplier": unit.multiplier,
    })
};

Implementing Into<Bson> for CustomUnit and using explicit trait signature:

impl Into<Bson> for CustomUnit {
    fn into(self) -> bson::Bson {
        bson::Bson::Document(doc! {
            "unit": self.unit,
            "multiplier": self.multiplier,
        })
    }
}

let doc = doc! {
    "name": name.clone(),
    "qty": qty,
    "unit": Into::<Bson>::into(unit), // unit.into(),
};

I just found out another simpler solution...
Just convert the existing struct instance into a BSON object.
But I think your solution is more appropriate

use mongodb::bson;
let doc = doc! {
    "name": name.clone(),
    "qty": qty,
    "unit": bson::to_bson(&self.my_unit).unwrap()
};
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.