所有権なしのスライス型
まず、独自スライス型等の DST については derive(serde::Deserialize)
は不可能である。
なぜなら、 Deserialize
は対象の型の値そのものを返すため、型のサイズがコンパイル時に判明していることを要求するからである。
仕方がないので手動で実装するほかない。
追加の制約なしの場合
#![allow(unused)] fn main() { // 追加の制約なしのスライス型での `Deserialize` 実装。 #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { use serde::de::Visitor; /// Visitor for `&MyStr`. struct StrVisitor; impl<'de> Visitor<'de> for StrVisitor { type Value = &'de MyStr; #[inline] fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("a string") } #[inline] fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { Ok(Self::Value::from(v)) } } deserializer.deserialize_str(StrVisitor) } } }
serde::Deserialize
は serde::Serialize
と同じく、作りたい型に対して実装するトレイトである。
今回作りたい型は独自スライス型自体ではなく、その参照型である。
実際、上の例では MyStr
自体ではなく &MyStr
に実装している。
参照型を作るとき serde への入力は mutable でない参照として渡されるため、 &mut MyStr
などへの実装はそもそも無理であり、考える必要はない。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { unimplemented!() } } }
serde にお決まりの形である。
詳しく考えたければ serde のドキュメントを参照した方が良いだろう。
serde::Deserialize<'de>
の 'de
は入力データの lifetime であるが、これが &'de MyStr
のように出力の型に表れているということは、すなわち入力データを他の場所にコピーすることなくそのまま参照できる (その可能性がある) ということを意味している。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&MyStr`. struct StrVisitor; unimplemented!() } } }
Deserialize
の実装には、 visitor という役割を果たす値が必要になり、通常は専用の型を用意する。
これをモジュール直下に定義しても良いのだが、そうするとたったひとつの関数でしか使わない型がモジュールにぶち撒けられることになり、あまり気持ちよくない。
あるいは実装を他の場所に移そうとしたとき、一緒に移動し忘れる可能性もある。
そこで、今回定義する visitor である StrVisitor
とそれへのトレイト実装は、 deserialize
関数内に書いてしまうことにする。
visitor は serde::de::Visitor
が実装された型で、入力データの一部である何かしらの値について、その型やフィールド名等の情報を適切に利用して Rust 上の目的の型へと変換する。
詳細については公式ドキュメントを参照。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&MyStr`. struct StrVisitor; impl<'de> Visitor<'de> for StrVisitor { type Value = &'de MyStr; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } } unimplemented!() } } }
今回実装する visitor は &'de MyStr
を作るためのものだから、関連型 Value
を &'de MyStr
とする。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&MyStr`. struct StrVisitor; impl<'de> Visitor<'de> for StrVisitor { type Value = &'de MyStr; #[inline] fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("a string") } } unimplemented!() } } }
Visitor::expecting()
は、 visitor が読もうとしている対象の型を端的に表現する文言を出力する。
読み込み失敗時に serde (deserializer) が「○○を読もうとしたけどだめだった」というエラーを返すが、この「○○」に該当する語句を出力するということである。
もし visitor が動的な情報や内部状態を持っていれば、たとえば write!(f, "an integer between {} and {}", self.min, self.max)
のように write!
マクロを使うのが楽かもしれない。
今回読みたい文字列型でそのような追加情報はないため、 Formatter::write_str()
で済ますことができる。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&MyStr`. struct StrVisitor; impl<'de> Visitor<'de> for StrVisitor { type Value = &'de MyStr; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } #[inline] fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { Ok(Self::Value::from(v)) } } unimplemented!() } } }
Visitor::visit_borrowed_str()
を実装することで、「入力データと同じ lifetime を持つ文字列からであれば、所望の型の値を作れる」と表明する。
作ろうとしている &'de MyStr
は入力データと同じ lifetime ('de
) を持つ型なので、このメソッドを必ず実装する必要がある。
Visitor::visit_str()
もあるが、これは「visit_str()
中のみ生きている文字列から、所望の型を作れる」という表明になる。
残念ながらこの場合、与えられた文字列は visit_str()
終了後に破棄が許されてしまうため、入力と同じ lifetime ('de
) を与えることはできない。
よって、このメソッドはデフォルト実装でエラーを返させる。
Visitor::visit_string()
についても同様に lifetime の不足の理由から手動での実装はしない。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&MyStr`. struct StrVisitor; impl<'de> Visitor<'de> for StrVisitor { type Value = &'de MyStr; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } #[inline] fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { // <StrVisitor as Visitor>::visit_borrowed_str() 内 Ok(Self::Value::from(v)) } } unimplemented!() } } }
ここで Self::Value
は <Self as serde::de::Visitor>::Value
と等価で、すなわち &'de MyStr
である。
つまり、 v: &'de str
について From<&'a str> for &'a MyStr
の実装を使うことで &'de MyStr
の値を作成する。
&'de MyStr
の作成は必ず成功するので、 Ok()
で包んで返す。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de MyStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&MyStr`. struct StrVisitor; impl<'de> Visitor<'de> for StrVisitor { type Value = &'de MyStr; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } } // <&MyStr as Deserialize>::deserialize() 内 deserializer.deserialize_str(StrVisitor) } } }
さて StrVisitor
の定義ができたので、肝心の deserialize()
の実装が書ける。
ここでは絶対に borrowed str しか受け付けない visitor を書いたため Deserializer::deserialize_str()
を利用しているが、もし複数種類の入力を受け付けたいのであれば Deserializer::deserialize_any()
を使うべきである (詳細は後述)。
最初からここで deserialize_any()
を使っても問題なく動くので、コピペミスが怖かったりマクロで自動生成したいなどであればそのようにするのが良いかもしれない。
一応挙動の理解に参考になりそうなテストも載せておこう。 解説はしないので、詳しくは serde のドキュメントやリファレンスを漁ってほしい。
#![allow(unused)] fn main() { // デシリアライズされた `&MyStr` は入力以下の生存期間しか持てないため、デシリアライズ完了とともに // 破棄される owned な文字列 (`"hello".to_owned()`) からは `&MyStr` を作ることができない。 #[cfg(feature = "serde")] #[cfg(test)] mod serde_tests { use super::*; use serde::de::{ value::{BorrowedStrDeserializer, Error}, Deserialize, IntoDeserializer, }; #[test] fn deserialize_borrowed_str() { let source_data = "hello"; let source_input = BorrowedStrDeserializer::<'_, Error>::new(source_data); let mystr: &MyStr = <&MyStr>::deserialize(source_input).unwrap(); assert_eq!(mystr.as_str(), source_data); } #[test] fn deserialize_owned_str() { let source_input = "hello".to_owned().into_deserializer(); let result: Result<&MyStr, Error> = <&MyStr>::deserialize(source_input); assert!( result.is_err(), "Deserialize is impossible when the source data does not have enough lifetime" ); } } }
追加の制約ありで単一の型からしか読まない場合
#![allow(unused)] fn main() { // 追加の制約ありで、文字列だけからデシリアライズする場合。 #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de AsciiStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&AsciiStr`. struct StrVisitor; impl<'de> serde::de::Visitor<'de> for StrVisitor { type Value = &'de AsciiStr; #[inline] fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("an ASCII string") } #[inline] fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { Self::Value::try_from(v).map_err(E::custom) } } deserializer.deserialize_str(StrVisitor) } } }
基本的に &MyStr
の場合と同じであるが、値の作成に失敗する可能性があるため、その場合のエラー処理は追加されている。
#![allow(unused)] fn main() { // 追加の制約ありで、文字列だけからデシリアライズする場合。 #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de AsciiStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&AsciiStr`. struct StrVisitor; impl<'de> serde::de::Visitor<'de> for StrVisitor { type Value = &'de AsciiStr; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { // <StrVisitor as Visitor>::visit_borrowed_str() 内 Self::Value::try_from(v).map_err(E::custom) } } deserializer.deserialize_str(StrVisitor) } } }
Self::Value
は &'de AsciiStr
であるから <&'de AsciiStr as TryFrom>::try_from()
で Result<&&'de AsciiStr, AsciiError>
を得るが、 visit_borrowed_str()
は Result<Self::Value, D::Error>
を返すため、エラー型を変換してやる必要がある。
ここで D
すなわち deserializer の型はデータ形式 (たとえば json 、たとえば toml) 次第なので、データ型にとって未知の型を想定する必要があり、 D: serde::Deserializer<'de>
を頼りに [Deserializer
][serde::Deserializer
] トレイト経由で操作するしかない。
問題のエラー型 D::Error
は [Deserializer::Error
][serde::Deserializer::Error
関連型]であるから、これも未知の型を想定して serde::de::Error
経由でしか操作が行えないようになっている。
serde::de::Error::custom()
は汎用的なエラーを返すもので、雑に使うことができる (あまりよろしくないが)。
ちなみに serde::de::Error::custom()
は渡されるメッセージに制約を課しているため、今回の例のような使い方をするなら本当は `
The message should not be capitalized and should not end with a period.
——serde v1.0.118 における
serde::de::Error::custom()
のドキュメント
また、エラーをもっと正確に表現したいのであれば、 serde::de::Error
の他のメソッドを使うことになる。
たとえば今回の例では、エラーの原因は型などではなく値の内容であるから、 serde::de::Error::invalid_value()
を使って書くこともできる。 本当はこちらを使う方が望ましいかもしれない。
#![allow(unused)] fn main() { // より詳細に意味付けされた形でエラーを返す例。 #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de AsciiStr { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&AsciiStr`. struct StrVisitor; impl<'de> serde::de::Visitor<'de> for StrVisitor { type Value = &'de AsciiStr; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { // <StrVisitor as Visitor>::visit_borrowed_str() 内 Self::Value::try_from(v) .map_err(|_| E::invalid_value(serde::de::Unexpected::Str(v), &self)) } } deserializer.deserialize_str(StrVisitor) } } }
追加の制約ありで複数の型から読む場合
AsciiBytes
では、 &str
からだけでなく &[u8]
からもデシリアライズできるようにしよう。
#![allow(unused)] fn main() { // 追加の制約ありで、文字列とバイナリからデシリアライズする場合。 #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de AsciiBytes { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&AsciiBytes`. struct BytesVisitor; impl<'de> serde::de::Visitor<'de> for BytesVisitor { type Value = &'de AsciiBytes; #[inline] fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("ASCII bytes") } #[inline] fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E> where E: serde::de::Error, { Self::Value::try_from(v).map_err(E::custom) } #[inline] fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E> where E: serde::de::Error, { Self::Value::try_from(v).map_err(E::custom) } } deserializer.deserialize_any(BytesVisitor) } } }
骨格はだいたい同じで、重要な違いは次の2箇所だけである。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de AsciiBytes { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&AsciiBytes`. struct BytesVisitor; impl<'de> serde::de::Visitor<'de> for BytesVisitor { type Value = &'de AsciiBytes; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } #[inline] fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E> where E: serde::de::Error, { Self::Value::try_from(v).map_err(E::custom) } } deserializer.deserialize_any(BytesVisitor) } } }
まず、バイト列用の visit_borrowed_bytes()
の実装を追加した。
これは visit_borrowed_str()
の str
でなく [u8]
版というだけなので、これ以上の説明は不要だろう。
#![allow(unused)] fn main() { #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for &'de AsciiBytes { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { /// Visitor for `&AsciiBytes`. struct BytesVisitor; impl<'de> serde::de::Visitor<'de> for BytesVisitor { type Value = &'de AsciiBytes; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unimplemented!() } } // <BytesVisitor as Deserialize>::deserialize() 内 deserializer.deserialize_any(BytesVisitor) } } }
それから、これまでの例では Deserializer::deserialize_str()
を使っていたが、今回は deserialize_any()
を使っている。
これは許容する型を単独で指定するのではなく、デシリアライザ (つまり読んでいるフォーマットについて知っている処理系) から型情報を受け取って、それに基いて値の作成を行うというものである。
今回の例では、たとえばデシリアライザがバイナリを読んだら BytesVisitor::visit_borrowed_bytes()
が呼ばれ、文字列を読んだら BytesVisitor::visit_borrowed_str()
が呼ばれるという挙動になる。