まず明確にしておかなけばならないのは、Copyトレイトを実装した型で行われる値のコピーと、Cloneトレイトを実装した型で行われる値のclone()は多くの場合は異なる操作だということです。
前者はメモリ上のバイト列を単純にコピーする操作で、Cのmemcpyに相当します。このような操作は一般的にはshallow copy(浅いコピー)と呼ばれます。
後者はその型に実装されたclone()メソッドを呼び出す操作です。どういうことをするかはclone()メソッドの実装に依存しますが、多くの場合はmemcpyよりも複雑なdeep copy(深いコピー)を行います。
たとえばString型は文字列データをVec<u8>型で持ちます。Vec<T>型はT型の各要素を格納するメモリ領域と、3つの要素を持つ構造体で表現されています。その構造体の要素は、前述のメモリ領域を指すポインタ、Vec<T>の容量(capacity)、Vec<T>の長さになっています。
仮にStringがCopyトレイトを実装できたとすると、shallow copyによってコピーされるのはVec<u8>の構造体の部分だけになります。u8型の各要素を格納するメモリ領域はコピーされず、また構造体の中にあるポインタも(memcpy相当なので)みな同じアドレスを指します。つまり、コピーによって作られた複数のStringが、同じu8型の要素を共有することになります。
Copyトレイトはmemcpy相当の操作だけで完全にコピーできる型にしか実装できないようになっています。たとえばu8型の値はmemcpyでコピーすれば十分(他にコピーするものがない)なのでCopyトレイトが実装されています。一方、Vec<T>はmemcpyでは不十分(shallow copyになる)なのでCopyトレイトを実装できません。
一方、Vec<T>のclone()メソッドはdeep copyを行うように実装されています。各要素を格納するメモリ領域が新たに割り当てられ、構造体の値も新たに作られます。また個々の要素も、そのclone()メソッドを呼ぶことでコピーされます。つまり、clone()によって作られた複数のStringは、それぞれが異なるメモリ領域にあるu8型の要素を持つことになります。
さて本題のRustの配列型の初期化構文[s; 2]では、なぜsのところにCopyトレイトを実装した型しか受け付けないかですが、その理由はドキュメントに書かれてないので、あくまでも私の推測を元に説明します。
Rustの配列はあらかじめ言語に組み込まれている型(プリミティブ型)です。また、初期化の構文[s; 2]も言語に組み込まれています。そのような構文からは、clone()のようにRustコードで実装されたメソッドを呼び出すことができないのかもしれません。メソッドは呼べないので、型の実装に依存しないmemcpy相当のコピーを行うのではないでしょうか。memcpy相当の操作で完全にコピーできることを保証するには、sがCopyトレイトを実装していなければなりません。
Vec<T>には配列の初期化にそっくりのvec![s; 2]の構文があります。Vec<T>はプリミティブ型ではなく、Rustコードで実装されたユーザー定義型です。またvec![s; 2]はマクロで実装されており、これもコンパイル時にRustコードに変換されます。そのためだと思いますが、vec![s; 2]ではsがCopyを実装している必要はなく、Cloneを実装していれば初期化できます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/04/15 15:51