RustのコピーセマンティクスをCopyトレイトを実装して確認する

Rustの勉強してます、所有権のところでムーブセマンティクスとコピーセマンティクスという概念が登場しました。独自定義の型で挙動の違いを確認してみました。
2019.09.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。サービスグループの武田です。

最近Rust勉強会が定期的に開催されていて、社内のRust熱(Rust愛?)が上がっています。

『The Rust Programming Language』勉強会 | シリーズ | DevelopersIO

Rustの特徴的なメモリ管理モデルとして所有権があります。正直まだ勉強し始めなので、はっきり理解したと自信をもって言えませんが、所有権については完全に理解した *1といっても過言ではないでしょう。

ムーブセマンティクスとコピーセマンティクス

Rustの値には所有権があり、その所有権をもっている変数を所有者と呼びます。ある値の所有者はただひとつの変数という制限があり、言い換えると同時に2つ以上の変数がある値の所有権をもつことはできない、ということになります。この制限によって、所有者がスコープから外れる際にメモリを安全に破棄できます。

ではこの所有権は一度取得したらずっとその変数が保持し続けるのかというとそんなことはありません。変数束縛(代入。関数の引数なども含む)の際に変わる可能性があります。

次のコードはString型を使った簡単なサンプルです。s1は文字列データに束縛されるとともに、所有権を保持します。次の行で_s2を束縛していますが、このとき文字列データ("Hello, Rust")の所有権が_s2に移動します。s1はすでに所有権をもたないためprintln!の行はコンパイルがとおりません。このように束縛で所有権が移動するのをムーブセマンティクスと呼びます。

Rust Playgroundで試す

次のコードは整数リテラルを使った簡単なサンプルです。n1は整数データに束縛されるとともに、所有権を保持します。次の行で_n2を束縛していますが、このとき整数データ(5)がコピーされ、_n2はコピーされた値の所有権を保持します。n1は引き続き所有権を保持しているためprintln!は問題なく実行できます。このように束縛で値がコピーされるのをコピーセマンティクスと呼びます。

Rust Playgroundで試す

Rustのデフォルトはムーブセマンティクス

2つの異なる挙動を確認しましたが、Rustのデフォルトの挙動はムーブセマンティクスです。コピーセマンティクスは変数束縛のたびに値がコピーされますし、そんなことになったら所有者はひとつの変数の意味がありません。

それではどこで挙動が分かれるかというと、Copyトレイトを実装しているか、です。対象の型がCopyトレイトを実装している場合、束縛時にコピーセマンティクスとなります。Rustのプリミティブ型はすべてCopyトレイトを実装しています。そのため整数リテラルを使ったサンプルではコピーセマンティクスとなったのでした。

Copyトレイトを実装してみる

前置きが長くなりましたが、ここまでの内容はTRPLを読んで学習した内容です。実際にCopyトレイトを実装することでコピーセマンティクスになることを確認しよう、というのがこのエントリの本題です。

Copyトレイトは事前条件としてCloneトレイトの実装が必要です。Cloneトレイトはcloneという明示的にオブジェクトを複製(deepcopy)するためのメソッドを提供します。またCloneトレイトは構造体のすべてのメンバーがclone可能な場合、#[derive]で宣言するだけで実装完了です。そしてCopyトレイトも実装することで、変数束縛時に内部ではcloneが呼ばれるのだと思われます。

というわけで確認するために書いてみたコードが次のものです。ExampleNonCopyTraitはCopyトレイトを実装していませんのでムーブセマンティクス。ExampleCopyTraitImplはCopyトレイトを実装しているのでコピーセマンティクス。となれば意図どおりです。

Rust Playgroundで試す

結果は意図したものになりました。Copyトレイトによってコピーセマンティクスになることが確認できました。

まとめ

Rust楽しいからみんなやろうぜ!

脚注