Почему я получаю ошибку UnsupportedType при сериализации в TOML с реализованной вручную сериализацией для перечисления с вариантами структуры?

Я пытаюсь реализовать Serialize для перечисления, включающего варианты структуры. В документации serde.rs указано следующее:

enum E {
    // Use three-step process:
    //   1. serialize_struct_variant
    //   2. serialize_field
    //   3. end
    Color { r: u8, g: u8, b: u8 },

    // Use three-step process:
    //   1. serialize_tuple_variant
    //   2. serialize_field
    //   3. end
    Point2D(f64, f64),

    // Use serialize_newtype_variant.
    Inches(u64),

    // Use serialize_unit_variant.
    Instance,
}

Имея это в виду, я приступил к реализации:

use serde::ser::{Serialize, SerializeStructVariant, Serializer};
use serde_derive::Deserialize;

#[derive(Deserialize)]
enum Variants {
    VariantA,
    VariantB { k: u32, p: f64 },
}

impl Serialize for Variants {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match *self {
            Variants::VariantA => serializer.serialize_unit_variant("Variants", 0, "VariantA"),
            Variants::VariantB { ref k, ref p } => {
                let mut state =
                    serializer.serialize_struct_variant("Variants", 1, "VariantB", 2)?;
                state.serialize_field("k", k)?;
                state.serialize_field("p", p)?;
                state.end()
            }
        }
    }
}

fn main() {
    let x = Variants::VariantB { k: 5, p: 5.0 };
    let toml_str = toml::to_string(&x).unwrap();
    println!("{}", toml_str);
}

Код компилируется, но когда я его запускаю, он терпит неудачу:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: UnsupportedType', src/libcore/result.rs:999:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Я решил, что проблема должна заключаться в моем использовании API, поэтому я проконсультировался с API документация для StructVariant и выглядит практически так же, как мой код. Я уверен, что что-то упускаю, но я не вижу этого на основе документов и вывода.


person Dash83    schedule 19.08.2019    source источник
comment
Что мешает вам получить Serialize как и большинство вариантов использования?   -  person Shepmaster    schedule 19.08.2019
comment
Он завершается с тем же значением ошибки: UnsupportedType. Предположительно, моя реализация делает то же самое, что и serde_derive? В этом случае, некоторые советы о том, что делать правильно, были бы очень признательны.   -  person Dash83    schedule 19.08.2019
comment
Возможно, вы можете отредактировать свой вопрос, чтобы показать, каким должен быть сгенерированный TOML?   -  person Shepmaster    schedule 19.08.2019
comment
На самом деле, я думаю, я пытался выяснить, каким будет результат сам.   -  person Dash83    schedule 19.08.2019


Ответы (2)


Включение внешних тегов для перечисления позволяет Serde сериализовать/десериализовать его в TOML:

#[derive(Deserialize)]
#[serde(tag = "type")]
enum Variants {
    VariantA,
    VariantB { k: u32, p: f64 },
}

toml::to_string(&Variants::VariantB { k: 42, p: 13.37 })

сериализуется в

type = VariantB
k = 42
p = 13.37

Это также хорошо работает в Vecs и HashMaps.

person masch    schedule 05.08.2020
comment
Спасибо за ваш ответ, если честно, я даже не помню, для чего я это использовал (спросил около года назад), но приятно знать, что это можно сделать! - person Dash83; 06.08.2020

Формат TOML не поддерживает перечисления со значениями:

use serde::Serialize; // 1.0.99
use toml; // 0.5.3

#[derive(Serialize)]
enum A {
    B(i32),
}

fn main() {
    match toml::to_string(&A::B(42)) {
        Ok(s) => println!("{}", s),
        Err(e) => eprintln!("Error: {}", e),
    }
}
Error: unsupported Rust type

Неясно, что вы хотите, чтобы ваша структура данных отображалась как TOML. Использование JSON работает отлично:

use serde::Serialize; // 1.0.99
use serde_json; // 1.0.40

#[derive(Serialize)]
enum Variants {
    VariantA,
    VariantB { k: u32, p: f64 },
}

fn main() {
    match serde_json::to_string(&Variants::VariantB { k: 42, p: 42.42 }) {
        Ok(s) => println!("{}", s),
        Err(e) => eprintln!("Error: {}", e),
    }
}
{"VariantB":{"k":42,"p":42.42}}
person Shepmaster    schedule 19.08.2019
comment
Вроде понятно, какой должна быть сериализация: [VariantB] k:42 p:42.42 - person clord; 24.09.2019