5

Rust で結果を JSON 文字列としてエンコードしようとしてdrain()います。vecこれを行うための最良の慣用的な方法は何ですか?

#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate serde_json;

#[derive(Serialize, Deserialize, Debug)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    pub fn new(x: i32, y: i32) -> Point {
        Point {
            x: x,
            y: y
        }
    }
}

fn main() {
    let mut points = vec![Point::new(1,2), Point::new(-2,-1), Point::new(0, 0)];
    let mut drain = points.drain(..);

    println!("{}", serde_json::to_string(&drain).unwrap());
}
4

1 に答える 1

6

イテレータのドレインは興味深い獣です。これらを使用すると、コレクション内のアイテムのすべてではなく一部の所有権を取得して、コレクションの一部をチャンクアウトできます。また、合理的に効率的な方法でこれを行うこともできます。たとえば、ベクトルは末尾のデータをまとめて 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::Serializetraitを実装するだけです。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です。

于 2015-12-21T16:36:33.577 に答える