MongoDB insert generic / dynamic struct field

Hello Community!
I'm new to rust and started learning by writing a web service with actix and mongodb.

Is there a way to have a struct field that can have different types depending on an enum value?

The enum is:

#[derive(Serialize, Deserialize, Debug, Display)]
#[serde(rename_all = "lowercase")]
pub enum ParameterType {
    #[strum(serialize="kilometer")]
    KILOMETER(i64),
    #[strum(serialize="review")]
    REVIEW(String)
}

The Schema is:

#[skip_serializing_none]
#[derive(Serialize, Deserialize)]
pub struct Schema<T> {
    pub _id: ObjectId,
    pub user_id: ObjectId,
    pub parameter_type: ParameterType,
    pub value: T
}

Let's say, I have a variable "p" of ParameterType - how can I set the generic based on that ParameterType, like:

    let p = ParameterType::KILOMETER;
    let schema = parameter::Schema<p> {
        _id: ObjectId::new(),
        user_id: authenticated_user._id,
        parameter_type: p,
        value: 7 // i64
    }

is there any way to achieve this?
An alternative would be to have something like an "std::any::Any" type that can be inserted to mongodb regardless of it's type, like - it can be a String or an i64...

thank you in advance!

You are passing an instance of ParameterType, not the ParameterType type. Since you are new to Rust I would suggest that you formulate your questions based on what you want to achieve, and not how you want to achieve it, because you are using patterns from other languages that are not replicable in Rust or are not the best solution to achieve the results you are looking for.

1 Like

sorry on this one.

what I want to achieve is to insert a document to mongodb based on a struct, that has a field with a dynamic type that can be for instance a String or an i64. Nevermind about the enum thing - it's just about having a field that can differ in type :>

Then create an Enum of Schemas:

In Rust, the algebraic sum type is the Enum, so you can represent a type that is either A or B. Rust enums are tagged, so you will always know what variant are you dealing with (i.e. KilometerSchema or ReviewSchema) because you will need to pattern match on it. When deserializing it from your MongoDB database, the deserializer will visit all of the enum variants until it finds one that matches. You could also use an internally tagged enum to further hint the deserializer:

https://serde.rs/enum-representations.html#internally-tagged

1 Like

oh my gosh this is it!! :heart_eyes:

it will not only validate that a certain type is only allowed for a certain enum key but also enable insertion to mongo with different types for the same value key. this is exactly what I thought of, thank you!

No need to even make an enum of Schemas, you can use #[serde(flatten)] on an adjacently-tagged enum to represent this:

#[derive(Serialize, Deserialize, Debug)]
pub struct Schema {
    pub _id: ObjectId,
    pub user_id: ObjectId,
    #[serde(flatten)]
    pub parameter: Parameter,
}

#[derive(Serialize, Deserialize, Debug)]
#[serde(tag = "parameter_type", content = "value")]
#[serde(rename_all = "snake_case")]  // maps Kilometer to "kilometer" and etc.
pub enum Parameter {
    Kilometer(i64),
    Review(String),
}

Playground link

3 Likes

this is the cherry on top. you helped me a lot, thank you!

I would really like to give both answers the "Solution" flag, but it won't let me. :<

Just a word of caution: Think how you'll consume your types when defining the database schema. Depending on your business logic you might want to have a single struct as per ExpHP's example, or multiple structs (like in my example).

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.