TL;DR

  • Send は値の所有権をスレッド間で渡せること、Sync は &T をスレッド間で渡せること(複数スレッドから安全に参照できること)を示すマーカトレイトである。
  • どちらも自動トレイトなので、実装していない型(Rc<T>、Cell<T>、RefCell<T>、生ポインタ)を押さえておけばよい。

内容

項目17 状態共有並列実行には気を付けようのうち、Send と Sync についての話である。

2 つのマーカトレイト

スレッド間での利用に関わるマーカトレイトが 2 つある。

  • std::marker::Send: 型 T の値をスレッド間で転送できることを示す。値の所有権をスレッドからスレッドへ渡せる。
  • std::marker::Sync: 型 T の値に、借用ルールの範囲内で複数のスレッドから安全にアクセスできることを示す。言い換えると、&T をスレッド間で転送できる。

実装していない型に注目する

Send と Sync は自動トレイトで、新しい型の構成要素がすべて Send / Sync を実装していれば、コンパイラが自動で実装する。ほとんどの安全な型は実装しているので、実装していない型(impl !Sync for ...)に注目するとよい。一覧は Sync の Implementors で確認できる。

Send を実装していない型は、1 つのスレッドの中でしか使えない。たとえば Rc<T> は、同期の仕組みを持たない参照カウントポインタである。実行速度を優先して、明示的にシングルスレッドを前提にしている。マルチスレッドで使いたいときは Arc<T> を使う。

Sync を実装していない型は、複数のスレッドから不変参照を通して安全にアクセスできない。たとえば Cell<T> や RefCell<T> は、同期せずに内部可変性を提供する。マルチスレッドで使いたいときは Mutex<T> や RwLock<T> を使う。

また、*const T や *mut T などの生ポインタの型も、Send と Sync を実装していない(項目34)。

キーワード