rust-cli / rust-cli/config-rs

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

Open
#720 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-env C-bug
Dominant language
Rust
Stars
3.2k
Forks
265
Avg merge
2h 42m
Merged PRs (30d)
4

Description

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 https://github.com/rust-cli/config-rs/issues/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();

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the environment-variable and set_default reproductions through Config::builder, Environment::with_prefix, build, and try_deserialize. Inspect how the type and value keys are inserted and deserialized, then verify that reversing their order produces the same result and that integer-valued variants behave consistently.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.