Building a Struct from a Vector of Enum Variants

I am using tokio to loop over and receive and parse various frames from a socket

When the receive loop is finished I have a vector of Enum variants

It looks roughly like the code below

let mut responses: Vec<Foo> = Vec::with_capacity(2);
// Now we loop over stream.next to get the the response
'receive_loop: loop {
    // We check if there is a frame on the socket
    if let Some(frame) = stream.next().await {

        match frame {
            Ok(frame) => {
                let parsed_message =
                    serde_json::from_str::<Foo>(frame.as_str()).unwrap();

                responses.push(parsed_message);

            }
            Err(e) => {
                // There's an IO error, so we'll stop
                println!("Error received: {}, while reading", e,);
                return Err(anyhow::Error::new(e));
            }
        }
    } else {
        // If we received a None that means the
        // connection was closed
        println!("Connection closed for client");
        // This ends the loop for this client
        break 'receive_loop;
    }
}

When the loop above breaks I have a vector of frames which are like the Foo enum below except instead of Strings they are enums of various other types

enum Foo {
    Bar {
        A: String,
        B: String,
        C: String,
    },
    Baz {
        D: String,
    },
}

let testA : Foo::Bar = Foo::Bar{
    A:"Some String",
    B:"Another String",
    C:"Third String"
}

let testB : Foo::Bar = Foo::Bar{
    D:"Fourth String",
}

let responses: Vec<Foo> = vec![testA,testB]

let new_struct = NewStruct{
    A:testA.A,
    B:testA.B,
    C:testA.C,
    D:testB.D,
}

I am struggling with the fact that enum variants are not a type so I am having difficulty extracting the Bar and Baz types from the vector and telling the compiler that they are the variant they are so I can access the inner values.

Is there a better approach to achieve the desired result of new_struct.

Ideally I guess I could build new_struct in the loop but I am not sure how to instantiate a struct with only partial info, would this be a builder pattern thing?

In this case it really looks like you don't want to use enums. An enum is a tool you need when a value can take the form of several unrelated alternatives dynamically. (The jargon is that it's a "sum" of the types of its variants.)

In this case, however, it seems to me that you either need to statically know the types of all the information you want to extract and group into your struct, or you do need the dynamism in which case you will need to perform manual dynamic type checking anyways.

(By the way, even if enum variants were types, for which there's a proposal as far as I know, that wouldn't help either, because you almost always need to check the enum discriminant in order to be able to promote it to a concrete variant.)

To spare yourself the somewhat tedious manual work, you could instead collapse the JSON from each frame somehow, e.g. collect them in a map-valued serde_json::Value with the appropriate keys, then attempt to deserialize the whole thing in one take, once you think you have all the necessary keys in your map.

Here's a playground demonstrating the idea.

1 Like

Thanks for the help H2CO3, especially the playground link, I really appreciate it.

It gives me a lot to think about.

-Jordan

I simplified the code a bit, at first I made it unnecessarily complicated for some reason (updated link in my original post).

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.