序文:この回答は、オプトインの組み込みトレイト(具体的にはCopy
アスペクト)が実装される前に作成されました。私は、ブロッククォートを使用して、古いスキーム(質問が尋ねられたときに適用されたスキーム)にのみ適用されたセクションを示しました。
旧:基本的な質問に答えるために、NoCopy
値を格納するマーカーフィールドを追加できます。例えば
struct Triplet {
one: int,
two: int,
three: int,
_marker: NoCopy
}
(Drop
traitを実装することにより)デストラクタを使用してそれを行うこともできますが、デストラクタが何も実行していない場合は、マーカータイプを使用することをお勧めします。
型はデフォルトで移動するようになりました。つまり、新しい型を定義すると、その型にCopy
明示的に実装しない限り実装されません。
struct Triplet {
one: i32,
two: i32,
three: i32
}
impl Copy for Triplet {} // add this for copy, leave it out for move
実装が存在できるのは、すべての型がnewに含まれるstruct
かenum
、それ自体である場合のみCopy
です。そうでない場合、コンパイラはエラーメッセージを出力します。また、型に実装がない場合にのみ存在しDrop
ます。
あなたが尋ねなかった質問に答えるために...「移動とコピーはどうしたの?」:
まず、2つの異なる「コピー」を定義します。
- バイトのコピーなどだけ浅くポインタ以下ではない、オブジェクトのバイト単位でのコピーされ、あなたが持っているならば
(&usize, u64)
、それは64ビットコンピュータ上で16バイトで、浅いコピーは、これらの16のバイトを取って、自分のを複製することになりますメモリのいくつかの他の16バイトのチャンクの値、無しタッチusize
の他方の端部&
。つまり、を呼び出すことと同じmemcpy
です。
- セマンティックコピー値を複製するには、安全に古いものに別々に使用することができ、新たな(やや)の独立したインスタンスを作成します。たとえば、anのセマンティックコピーは
Rc<T>
、参照カウントを増やすだけで、aのセマンティックコピーはVec<T>
、新しい割り当てを作成し、保存されている各要素を古いものから新しいものに意味的にコピーします。これらは、とすることができるディープコピー(例えばVec<T>
)または浅い(例えば、Rc<T>
接触していない保存されたT
)、Clone
仕事の最小量は、意味的タイプの値をコピーするために必要に応じて緩く定義されているT
内部から&T
のT
。
RustはCのようなもので、値の値による使用はすべてバイトコピーです。
let x: T = ...;
let y: T = x; // byte copy
fn foo(z: T) -> T {
return z // byte copy
}
foo(y) // byte copy
それらは、T
移動するかどうかに関係なくバイトコピーであるか、「暗黙的にコピー可能」です。(明確にするために、これらは必ずしも実行時に文字どおりバイト単位のコピーであるとは限りません。コードの動作が保持されている場合、コンパイラーはコピーを自由に最適化できます。)
ただし、バイトコピーには根本的な問題があります。メモリ内の値が重複することになり、デストラクタがある場合は非常に悪い可能性があります。
{
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
} // destructors run here
のw
単純なバイトコピーであるv
場合、同じ割り当てを指す2つのベクトルがあり、両方ともそれを解放するデストラクタを使用して... 問題が発生する二重解放を引き起こします。NB。v
intoのセマンティックコピーを実行した場合、これは完全に問題ありません。w
それw
は、それ自体が独立していてVec<u8>
、デストラクタが互いに踏みにじっていないためです。
ここにいくつかの可能な修正があります:
- プログラマにCのように処理させます(Cにはデストラクタがないため、それほど悪くありません...代わりにメモリリークが残ります。:P)
- セマンティックコピーを暗黙的に実行
w
します。これにより、コピーコンストラクターを使用するC ++のように、独自の割り当てが行われます。
- 値による使用を所有権の譲渡と見なすため、
v
使用できなくなり、デストラクタが実行されなくなります。
最後は、Rustが行うことです。移動は、ソースが静的に無効化される値による使用にすぎないため、コンパイラーは無効になったメモリをそれ以上使用できなくなります。
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
println!("{}", v); // error: use of moved value
デストラクタを持つ型は、値(バイトコピー時)として使用されるときに移動する必要があります。これは、リソース(メモリ割り当てやファイルハンドルなど)の管理/所有権があり、バイトコピーがこれを正しく複製する可能性が非常に低いためです。所有。
「ええと…暗黙のコピーとは何ですか?」
次のようなプリミティブ型について考えてみてくださいu8
。バイトコピーは単純で、シングルバイトをコピーするだけで、セマンティックコピーも同じくらい単純で、シングルバイトをコピーします。特に、バイトコピーはセマンティックコピーです... Rustには、どのタイプが同一のセマンティックコピーとバイトコピーを持つかをキャプチャする組み込みの特性Copy
もあります。
したがって、これらのCopy
タイプの場合、値による使用は自動的にセマンティックコピーでもあるため、ソースを継続して使用しても完全に安全です。
let v: u8 = 1;
let w: u8 = v;
println!("{}", v); // perfectly fine
Old:NoCopy
マーカーは、コンパイラーの自動動作をオーバーライドして、可能なタイプCopy
(つまり、プリミティブとの集合体のみを含む&
)がであると想定しますCopy
。ただし、オプトインの組み込みトレイトが実装されると、これは変更されます。
上記のように、オプトインの組み込みトレイトが実装されているため、コンパイラーは自動動作しなくなりました。ただし、以前の自動動作に使用されていたルールは、実装が正当かどうかを確認するためのルールと同じCopy
です。