イテレータのドレインは興味深い獣です。これらを使用すると、コレクション内のアイテムのすべてではなく一部の所有権を取得して、コレクションの一部をチャンクアウトできます。また、合理的に効率的な方法でこれを行うこともできます。たとえば、ベクトルは末尾のデータをまとめて 1 つのmemcpy
.
ただし、serde はイテレータのシリアル化をネイティブにサポートしていません (これには十分な理由がありますので、読み続けてください)。Serialize
トレイトを見て、それがサポートするもののタイプを確認できます。
これを自分で実装する必要があります:
use serde::{Deserialize, Serialize}; // 1.0.101
use std::{cell::RefCell, vec};
struct DrainIteratorAdapter<'a, T>(RefCell<vec::Drain<'a, T>>);
impl<'a, T: 'a> serde::Serialize for DrainIteratorAdapter<'a, T>
where
T: serde::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(self.0.borrow_mut().by_ref())
}
}
fn main() {
let mut points = vec![Point::new(1, 2), Point::new(-2, -1), Point::new(0, 0)];
let adapter = DrainIteratorAdapter(RefCell::new(points.drain(..)));
println!("{}", serde_json::to_string(&adapter).unwrap());
}
核となる難しい部分は、シリアライゼーションには副作用がないと想定されているということです。これは非常に合理的な決定です。next
ただし、反復子を呼び出すたびに、状態を更新するために変更する必要があります。これらの 2 つのミスマッチな概念を組み合わせるには、 のようなものを使用する必要がありRefCell
ます。
それ以上は、serde::Serialize
traitを実装するだけです。serde::Serialize
もも所有していないため、実装を配置するためにnewtypevec::Drain
を作成する必要があります。
このソリューションを一般化して、任意の反復子に適用できます。私の意見では、これによりたまたま読みやすくなります。
use serde::{Deserialize, Serialize}; // 1.0.101
use std::cell::RefCell;
struct IteratorAdapter<I>(RefCell<I>);
impl<I> IteratorAdapter<I> {
fn new(iterator: I) -> Self {
Self(RefCell::new(iterator))
}
}
impl<I> serde::Serialize for IteratorAdapter<I>
where
I: Iterator,
I::Item: serde::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_seq(self.0.borrow_mut().by_ref())
}
}
このソリューションの欠点は何ですか? 同じ値を 2 回シリアル化すると、結果が異なります。単純にシリアル化して値を 2 回出力すると、次のようになります。
[{"x":1,"y":2},{"x":-2,"y":-1},{"x":0,"y":0}]
[]
これは、反復子が一時的な獣であるためです。1 つの値を読み取ると、それはなくなります。これは、あなたが陥るのを待っている素敵な罠です。
あなたの例では、これは本当に意味がありません。全体にアクセスできるVec
ので、その時点でそれ (またはそのスライス) をシリアル化することもできます。drain
さらに、コレクション全体に (今のところ) 理由はありません。これは、 を呼び出すだけと同等into_iter
です。