【Rust】serdeでPhantomDataを扱う

serdeにおいてPhantomData型はデフォルトでは空の値として扱われ，例えば以下の`Video`型を
```rust
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Video<T> {
    file_type: PhantomData<T>,
    file_name: String,
}

impl<T> Video<T> {
    fn new(file_name: String) -> Video<T> {
        Video {
            file_name,
            file_type: PhantomData,
        }
    }
}

#[derive(Debug, PartialEq)]
struct Mp4;

#[derive(Debug, PartialEq)]
struct Avi;
```
jsonにシリアライズした場合，`file_type`は`null`になります．デシリアライズの際は`null`が任意の`PhantomData`型になります．

```rust
let video = Video::<Mp4>::new("my video 1".to_string());
assert_eq!(
	serde_json::to_string(&video).unwrap(),
	r#"{"file_type":null,"file_name":"my video 1"}"#
);

let video_json = r#"{"file_type":null,"file_name":"my video 2"}"#;
assert_eq!(
	serde_json::from_str::<Video<Avi>>(video_json).unwrap(),
	Video::<Avi>::new("my video 2".to_string())
)
```

多くの場合はデフォルトの挙動が望ましいと思いますが，他の言語でも扱う場合など`PhantomData`を文字列等の他の型へ変換したいこともあると思います．今回は`PhantomData`を文字列としてシリアライズ・デシリアライズする方法を二つ紹介します．コード全体は[こちら](https://github.com/deepgreenAN/serde_phantomdata_example).


まず`Mp4`，`Avi`などのUnitタイプに`TryFrom<String>`と`Into<String>`を実装します．`Clone`トレイトは後者の方法のみで必要です．`Default`トレイトは`PhantomData`から文字列に変換するときに利用します．

```rust
#[derive(Clone, Debug, Default, PartialEq)]
struct Mp4;

impl TryFrom<String> for Mp4 {
    type Error = String;
    fn try_from(value: String) -> Result<Self, Self::Error> {
        if value.as_str() == stringify!(Mp4) {
            Ok(Mp4)
        } else {
            Err("Parse Error".to_string())
        }
    }
}

impl From<Mp4> for String {
    fn from(_: Mp4) -> Self {
        stringify!(Mp4).to_string()
    }
}

#[derive(Clone, Debug, Default, PartialEq)]
struct Avi;

impl TryFrom<String> for Avi {
    type Error = String;
    fn try_from(value: String) -> Result<Self, Self::Error> {
        if value.as_str() == stringify!(Avi) {
            Ok(Avi)
        } else {
            Err("Parse Error".to_string())
        }
    }
}

impl From<Avi> for String {
    fn from(_: Avi) -> Self {
        stringify!(Avi).to_string()
    }
}
```
## serde_withを利用する
`serde(serialize_with = "path")`と`serde(deserialize_with = "path")`フィールドアトリビュートを指定することでシリアライズとデシリアライズを特定の関数で行うことができます．
以下の関数では`PhantomData<T>`をシリアライズ・デシリアライズしています．`serialize_phantom`では`Default`トレイトを使って与えた型パラメーターから値を作成，文字列に変換しています．`deserialize_phantom`では与えた型パラメーターに文字列が変換できるかチェックしています．`serde::de::Error::custom`では`Display`トレイトを実装した任意の値を渡してエラーを作成できます．

```rust
use serde::{Deserializer, Serializer};

fn serialize_phantom<S, T>(_: &PhantomData<T>, s: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
    T: Into<String> + Default,
{
    let type_str: String = T::default().into();
    s.serialize_str(&type_str)
}

fn deserialize_phantom<'de, D, T>(d: D) -> Result<PhantomData<T>, D::Error>
where
    D: Deserializer<'de>,
    T: TryFrom<String>,
{
    let unit_type_str: String = Deserialize::deserialize(d)?;
    let _unit_type: T = unit_type_str
        .try_into()
        .map_err(|_| serde::de::Error::custom("Parse Error".to_string()))?;

    Ok(PhantomData)
}
```

serdeのアトリビュートは以下のようにします．関数でシリアライズ・デシリアライズを行うため，型パラメーターのトレイト境界ももとの`Serialize, Deserialize`からその関数に合うように変更します．

```rust
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Video<T> {
    #[serde(
        serialize_with = "serialize_phantom",
        deserialize_with = "deserialize_phantom"
    )]
    #[serde(bound(serialize = "T: Into<String> + Default"))]
    #[serde(bound(deserialize = "T: TryFrom<String>"))]
    file_type: PhantomData<T>,
    file_name: String,
}
```
これによって，以下のように文字列としてシリアライズ・デシリアライズされます．
```rust
let video = Video::<Mp4>::new("my video 1".to_string());
assert_eq!(
	serde_json::to_string(&video).unwrap(),
	r#"{"file_type":"Mp4","file_name":"my video 1"}"#
);

let video_json = r#"{"file_type":"Avi","file_name":"my video 2"}"#;
assert_eq!(
	serde_json::from_str::<Video<Avi>>(video_json).unwrap(),
	Video::<Avi>::new("my video 2".to_string())
)
```

## NewTypeパターンを使う
`PhantomData`型をラップした新しい型`SerdePhantom`を定義し，`SerdePhantom`に文字列との変換用のトレイトを実装します．`serde(try_from = "FromType")`と`serde(into = "IntoType")`コンテナアトリビュートを指定することでシリアライズの前とデシリアライズの後に別の型を経由するようになります．`serde(into = "IntoType")`を利用するには`Clone`トレイトが必要となります．

```rust
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
#[serde(bound(serialize = "T: Default + Into<String> + Clone"))]
#[serde(bound(deserialize = "T: TryFrom<String>"))]
struct SerdePhantomData<T>(PhantomData<T>);

impl<T> TryFrom<String> for SerdePhantomData<T>
where
    T: TryFrom<String>,
{
    type Error = String;
    fn try_from(value: String) -> Result<Self, Self::Error> {
        let _unit_type: T = value.try_into().map_err(|_| "Parse Error".to_string())?;
        Ok(SerdePhantomData(PhantomData))
    }
}

impl<T> From<SerdePhantomData<T>> for String
where
    T: Default + Into<String>,
{
    fn from(_value: SerdePhantomData<T>) -> Self {
        T::default().into()
    }
}
```

`Video`側でも型パラメーターのトレイト境界を変更します．

```rust
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Video<T> {
    #[serde(bound(serialize = "T: Default + Into<String> + Clone"))]
    #[serde(bound(deserialize = "T: TryFrom<String>"))]
    file_type: SerdePhantomData<T>,
    file_name: String,
}

impl<T> Video<T> {
    fn new(file_name: String) -> Video<T> {
        Video {
            file_name,
            file_type: SerdePhantomData(PhantomData),
        }
    }
}
```

jsonとの変換は前者と全く同じように利用できます．

```rust
let video = Video::<Mp4>::new("my video 1".to_string());
assert_eq!(
	serde_json::to_string(&video).unwrap(),
	r#"{"file_type":"Mp4","file_name":"my video 1"}"#
);

let video_json = r#"{"file_type":"Avi","file_name":"my video 2"}"#;
assert_eq!(
	serde_json::from_str::<Video<Avi>>(video_json).unwrap(),
	Video::<Avi>::new("my video 2".to_string())
)
```