注意: 最新版のドキュメントをご覧ください。この第2版ドキュメントは古くなっており、最新情報が反映されていません。リンク先のドキュメントが現在の Rust の最新のドキュメントです。

SyncSendトレイトで拡張可能な並行性

面白いことに、Rust言語には、少な並行性機能があります。この章でここまでに語った並行性機能のほとんどは、 標準ライブラリの一部であり、言語ではありません。並行性を扱う選択肢は、言語や標準ライブラリに制限されません; 独自の並行性機能を書いたり、他人が書いたものを利用したりできるのです。

ですが、2つの並行性概念が言語に埋め込まれています: std::markerトレイトのSyncSendです。

Sendでスレッド間の所有権の転送を許可する

Sendマーカートレイトは、Sendを実装した型の所有権をスレッド間で転送できることを示唆します。 Rustのほとんどの型はSendですが、Rc<T>を含めて一部例外があります: この型は、Rc<T>の値をクローンし、 クローンしたものの所有権を別のスレッドに転送しようとしたら、両方のスレッドが同時に参照カウントを更新できてしまうので、 Sendになり得ません。このため、Rc<T>はスレッド安全性のためのパフォーマンスの犠牲を支払わなくても済む、 シングルスレッド環境で使用するために実装されているわけです。

故に、Rustの型システムとトレイト境界により、Rc<T>の値を不安全にスレッド間で誤って送信することが絶対ないよう保証してくれるのです。 リスト16-14でこれを試みた時には、the trait Send is not implemented for Rc<Mutex<i32>>というエラーが出ました。 SendArc<T>に切り替えたら、コードはコンパイルできたわけです。

完全にSendの型からなる型も全て自動的にSendと印付けされます。生ポインタを除くほとんどの基本型もSendで、 生ポインタについては第19章で議論します。

Syncで複数のスレッドからのアクセスを許可する

Syncマーカートレイトは、Syncを実装した型は、複数のスレッドから参照されても安全であることを示唆します。 言い換えると、&T(Tへの参照)がSendなら、型TSyncであり、参照が他のスレッドに安全に送信できることを意味します。 Send同様、基本型はSyncであり、Syncの型からのみ構成される型もまたSyncです。

Sendではなかったのと同じ理由で、スマートポインタのRc<T>もまたSyncではありません。 RefCell<T>型(これについては第15章で話しました)と関連するCell<T>系についてもSyncではありません。 RefCell<T>が実行時に行う借用チェックの実装は、スレッド安全ではないのです。 スマートポインタのMutex<T>Syncで、「複数のスレッド間でMutex<T>を共有する」節で見たように、 複数のスレッドでアクセスを共有するのに使用することができます。

SendSyncを手動で実装するのは非安全である

SendSyncトレイトから構成される型は自動的にSendSyncにもなるので、 それらのトレイトを手動で実装する必要はありません。マーカートレイトとして、 実装すべきメソッドさえも何もありません。並行性に関連する不変条件を強制することに役立つだけなのです。

これらのトレイトを手動で実装するには、unsafeなRustコードを実装することが関わってきます。 unsafeなRustコードを使用することについては第19章で語ります; とりあえず、重要な情報は、 SendSyncではない部品からなる新しい並行な型を構成するには、安全性保証を保持するために、 注意深い思考が必要になるということです。The Rustonomiconには、 これらの保証とそれを保持する方法についての情報がより多くあります。

訳注: 日本語版のThe Rustonomiconはこちらです。

まとめ

この本において並行性を見かけるのは、これで最後ではありません: 第20章のプロジェクトでは、 この章の概念をここで議論した微小な例よりもより現実的な場面で使用するでしょう。

前述のように、Rustによる並行性の取扱いのごく一部のみが言語仕様なので、多くの並行性の解決策は クレートとして実装されています。これらは標準ライブラリよりも迅速に進化するので、 マルチスレッド環境で使用すべき現在の最先端のクレートを必ずネットで検索してください。

Rustの標準ライブラリは、メッセージ受け渡しにチャンネルを、並行の文脈で安全に使用できる、 Mutex<T>Arc<T>などのスマートポインタ型を提供しています。型システムと借用チェッカーにより、 これらの解決策を使用するコードがデータ競合や無効な参照に行き着かないことを保証してくれます。 一旦コードをコンパイルすることができたら、他の言語ではありふれている追跡困難な類のバグなしに、 複数のスレッドでも喜んで動くので安心できます。並行プログラミングは、もはや恐れるべき概念ではありません: 恐れることなく前進し、プログラムを並行にしてください!

次は、Rustプログラムが肥大化するにつれて問題をモデル化し、解決策を構造化する慣例的な方法について話します。 さらに、Rustのイディオムがオブジェクト指向プログラミングで馴染み深いかもしれないイディオムにどのように関連しているかについても議論します。