Skip to content

Indeterministic errors when deserializing adjacently tagged enums storing integers based on string values #720

@benediktsatalia

Description

@benediktsatalia

We have come across an error occurring sometimes, when we want to deserialise a configuration containing an adjecently tagged enum. The erorr occurs if the source is a string, i.e. for example coming from an environment variable.

This is a small example to reproduce:

use config::Config;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(tag = "type", content = "value")]
enum A {
    V1(u64),
    V2(String),
}

fn main() {
    let config = Config::builder()
        .add_source(config::Environment::with_prefix("APP").separator("_"))
        .build()
        .unwrap();
    let a = config.try_deserialize::<A>().unwrap();
}

Then I run this with the following environment variables:

APP_type=V1 APP_value=42 cargo run

This errors 50% of the time and runs through successfully the other 50%. The error message is:

called `Result::unwrap()` on an `Err` value: invalid type: string "42", expected u64

I debugged this and the error occurs if the value key gets stored in the internal hashmap before the type key, therefore the indeterminim stems from the indeterministic nature of the ordering in hashmaps. This might be related to #442 but I don't think a solution is to just make it deterministic. I would guess that either it is supported to parse strings into variants that store integers, or it is not, it should not depend on the ordering of the tag versus the content.

If one wants to reproduce this without environment variables one can use:

let config = Config::builder()
    .set_default("type", "V1")
    .unwrap()
    .set_default("value", "42")
    .unwrap()
    .build()
    .unwrap();

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions