Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

rustc におけるシリアライズ

rustc は、コンパイル中にさまざまなデータをシリアライズおよびデシリアライズする必要があります。 具体的には、次のとおりです。

  • 主にクエリ出力で構成される「クレートメタデータ」は、ライブラリクレートのコンパイル時に出力される rlib ファイルおよび rmeta ファイルへ、バイナリ形式でシリアライズされます。これらの rlib ファイルおよび rmeta ファイルは、そのライブラリに依存するクレートによってデシリアライズされます。
  • 特定のクエリ出力は、インクリメンタルコンパイル結果を永続化するために、バイナリ形式でシリアライズされます。
  • CrateInfo-Z no-link フラグが使用されると JSON へシリアライズされ、-Z link-only フラグが使用されると JSON からデシリアライズされます。

Encodable トレイトと Decodable トレイト

rustc_serialize クレートは、シリアライズ可能な型のために 2 つのトレイトを定義しています。

pub trait Encodable<S: Encoder> {
    fn encode(&self, s: &mut S) -> Result<(), S::Error>;
}

pub trait Decodable<D: Decoder>: Sized {
    fn decode(d: &mut D) -> Result<Self, D::Error>;
}

また、整数型、浮動小数点型、boolcharstr など、さまざまな一般的な標準ライブラリのプリミティブ型に対するこれらの実装も定義しています。

それらの型から構築される型については、EncodableDecodable は通常 derive によって実装されます。これらは、構造体または enum のフィールドへデシリアライズを委譲する実装を生成します。構造体の場合、それらの impl はおおよそ次のようになります。

#![feature(rustc_private)]
extern crate rustc_serialize;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};

struct MyStruct {
    int: u32,
    float: f32,
}

impl<E: Encoder> Encodable<E> for MyStruct {
    fn encode(&self, s: &mut E) -> Result<(), E::Error> {
        s.emit_struct("MyStruct", 2, |s| {
            s.emit_struct_field("int", 0, |s| self.int.encode(s))?;
            s.emit_struct_field("float", 1, |s| self.float.encode(s))
        })
    }
}

impl<D: Decoder> Decodable<D> for MyStruct {
    fn decode(s: &mut D) -> Result<MyStruct, D::Error> {
        s.read_struct("MyStruct", 2, |d| {
            let int = d.read_struct_field("int", 0, Decodable::decode)?;
            let float = d.read_struct_field("float", 1, Decodable::decode)?;

            Ok(MyStruct { int, float })
        })
    }
}

アリーナに割り当てられた型のエンコードとデコード

rustc には多数のアリーナに割り当てられた型があります。 これらの型をデシリアライズするには、それらを割り当てる必要があるアリーナへのアクセスが必要です。 TyDecoder トレイトと TyEncoder トレイトは、TyCtxt へのアクセスを可能にする DecoderEncoder のサブトレイトです。

arena に割り当てられた型を含む型は、その Encodable および Decodable 実装の型パラメーターを、これらのトレイトで境界付けることができます。 例:

impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for MyStruct<'tcx> {
    /* ... */
}

TyEncodable および TyDecodablederive マクロは、そのような実装へ展開されます。

実際の arena に割り当てられた型のデコードはより困難です。なぜなら、一部の実装は孤児ルールのために書けないからです。これを回避するために、RefDecodable トレイトが rustc_middle で定義されています。これは任意の型に対して実装できます。TyDecodable マクロは参照をデコードするために RefDecodable を呼び出しますが、さまざまなジェネリックコードでは、型が特定のデコーダーで実際に Decodable である必要があります。

インターン化された型については、RefDecodable を手動で実装する代わりに、ty::Predicate のような newtype ラッパーを使用し、EncodableDecodable を手動で実装するほうが簡単な場合があります。

Derive マクロ

[rustc_macros] クレートは、DecodableEncodable の実装を助けるさまざまな derive を定義しています。

  • Encodable マクロと Decodable マクロは、すべての EncodersDecoders に適用される実装を生成します。これらは、rustc_middle に依存しないクレート、または TyEncoder を実装していない型によってシリアライズされる必要があるクレートで使用すべきです。
  • [MetadataEncodable] は、[rustc_metadata::rmeta::encoder::EncodeContext] によるデコードのみを許可する実装を生成します。
  • [BlobDecodable] と [LazyDecodable] は、MetadataEncodable に対応するデコード側として機能します。これらは rustc_metadata::rmeta 内のメタデータ blob デコーダーでデコードする実装を生成します。型に遅延メタデータハンドルがない場合は BlobDecodable を使用し、ある場合は LazyDecodable を使用します。
  • TyEncodableTyDecodable は、任意の TyEncoder または TyDecoder に適用される実装を生成します。これらは、クレートメタデータやインクリメンタルキャッシュでのみシリアライズされる型に使用すべきです。これは rustc_middle 内のシリアライズ可能な型の大半に該当します。 [BlobDecodable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/derive.BlobDecodable.html [LazyDecodable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/derive.LazyDecodable.html [MetadataEncodable]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_macros/derive.MetadataEncodable.html [rustc_macros]: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_macros [rustc_metadata::rmeta]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/index.html [rustc_metadata::rmeta::encoder::EncodeContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/encoder/struct.EncodeContext.html rustc_middle: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_middle

短縮表記

Ty は深く再帰的になる可能性があり、各 Ty を素朴にエンコードすると、クレートメタデータは非常に大きくなります。これに対処するため、各 TyEncoder は、型をシリアライズした出力内の位置のキャッシュを持ちます。エンコード中の型がキャッシュ内にある場合、通常どおり型をシリアライズする代わりに、書き込み中のファイル内のバイトオフセットがエンコードされます。同様の方式が ty::Predicate にも使用されます。

LazyValue<T>

クレートメタデータは TyCtxt<'tcx> が作成される前に最初に読み込まれるため、一部のデシリアライズはメタデータの初期読み込みから遅延させる必要があります。LazyValue<T> 型は、T がシリアライズされているクレートメタデータ内の(相対)オフセットをラップします。また、いくつかのバリアント、LazyArray<T>LazyTable<I, T> もあります。

LazyArray<[T]> 型と LazyTable<I, T> 型は、Lazy<Vec<T>>Lazy<HashMap<I, T>> に対していくつかの機能を提供します。

  • 先に Vec<T> に収集することなく、Iterator から直接 LazyArray<T> をエンコードできます。
  • LazyTable<I, T> へのインデックス指定では、読み取っているもの以外のエントリをデコードする必要はありません。

: LazyValue<T> は、初回デシリアライズ後にその値をキャッシュしません。代わりに、クエリシステム自体がこれらの結果をキャッシュする主な方法です。

特殊化

いくつかの型、とりわけ DefId は、異なる Encoder ごとに異なる実装を持つ必要があります。これは現在、アドホックな特殊化によって処理されています。例: DefId には Encodable<E>default 実装と、Encodable<CacheEncoder> に特化した実装があります。