Préface : Cette réponse a été rédigée avant la mise en œuvre des caractéristiques intégrées opt-in (en particulier les Copy
aspects) . J'ai utilisé des guillemets pour indiquer les sections qui ne s'appliquaient qu'à l'ancien schéma (celui qui s'appliquait lorsque la question était posée).
Ancien : pour répondre à la question de base, vous pouvez ajouter un champ de marqueur stockant une NoCopy
valeur . Par exemple
struct Triplet {
one: int,
two: int,
three: int,
_marker: NoCopy
}
Vous pouvez également le faire en ayant un destructeur (via l'implémentation du Drop
trait ), mais l'utilisation des types de marqueurs est préférable si le destructeur ne fait rien.
Les types se déplacent maintenant par défaut, c'est-à-dire que lorsque vous définissez un nouveau type, il n'implémente pas à Copy
moins que vous ne l'implémentiez explicitement pour votre type:
struct Triplet {
one: i32,
two: i32,
three: i32
}
impl Copy for Triplet {} // add this for copy, leave it out for move
L'implémentation ne peut exister que si chaque type contenu dans le nouveau struct
ou enum
est lui-même Copy
. Sinon, le compilateur imprimera un message d'erreur. Il ne peut également exister que si le type n'existe pas d' Drop
implémentation.
Pour répondre à la question que vous n'avez pas posée ... "Que se passe-t-il avec les mouvements et la copie?":
Tout d'abord, je définirai deux "copies" différentes:
- une copie d'octets , qui est juste une copie superficielle d'un objet octet par octet, sans suivre les pointeurs, par exemple si vous avez
(&usize, u64)
, c'est 16 octets sur un ordinateur 64 bits, et une copie superficielle prendrait ces 16 octets et répliquerait leur valeur dans un autre bloc de mémoire de 16 octets, sans toucher usize
à l'autre extrémité du &
. Autrement dit, cela équivaut à appeler memcpy
.
- une copie sémantique , dupliquant une valeur pour créer une nouvelle instance (quelque peu) indépendante qui peut être utilisée en toute sécurité séparément de l'ancienne. Par exemple, une copie sémantique de an
Rc<T>
implique simplement d'augmenter le nombre de références, et une copie sémantique de a Vec<T>
implique la création d'une nouvelle allocation, puis la copie sémantique de chaque élément stocké de l'ancien vers le nouveau. Ceux-ci peuvent être des copies profondes (par exemple Vec<T>
) ou superficielles (par exemple, Rc<T>
ne touche pas le stocké T
), Clone
est défini de manière approximative comme la plus petite quantité de travail requise pour copier sémantiquement une valeur de type T
de l'intérieur de a &T
à T
.
Rust est comme C, chaque utilisation par valeur d'une valeur est une copie d'octets:
let x: T = ...;
let y: T = x; // byte copy
fn foo(z: T) -> T {
return z // byte copy
}
foo(y) // byte copy
Ce sont des copies d'octets, qu'elles soient ou non T
déplacées ou "implicitement copiables". (Pour être clair, ils ne sont pas nécessairement des copies octet par octet au moment de l'exécution: le compilateur est libre d'optimiser les copies si le comportement du code est conservé.)
Cependant, il y a un problème fondamental avec les copies d'octets: vous vous retrouvez avec des valeurs dupliquées en mémoire, ce qui peut être très mauvais si elles ont des destructeurs, par exemple
{
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
} // destructors run here
Si w
c'était juste une copie d'octet simple de v
alors il y aurait deux vecteurs pointant vers la même allocation, tous deux avec des destructeurs qui la libèrent ... provoquant un double free , ce qui est un problème. NB. Ce serait parfaitement bien, si nous faisions une copie sémantique de v
into w
, puisque ce w
serait alors son propreVec<u8>
et les destructeurs ne se piétineraient pas les uns les autres.
Il y a quelques correctifs possibles ici:
- Laissez le programmeur le gérer, comme C. (il n'y a pas de destructeurs en C, donc ce n'est pas aussi grave ... vous vous retrouvez juste avec des fuites de mémoire à la place.: P)
- Effectuez une copie sémantique implicitement, de sorte qu'elle
w
ait sa propre allocation, comme C ++ avec ses constructeurs de copie.
- Considérez les utilisations par valeur comme un transfert de propriété, de sorte que
v
cela ne puisse plus être utilisé et que son destructeur ne soit pas exécuté.
Le dernier est ce que fait Rust: un déplacement est juste une utilisation par valeur où la source est statiquement invalidée, de sorte que le compilateur empêche toute utilisation ultérieure de la mémoire désormais invalide.
let v: Vec<u8> = vec![1, 2, 3];
let w: Vec<u8> = v;
println!("{}", v); // error: use of moved value
Les types qui ont des destructeurs doivent se déplacer lorsqu'ils sont utilisés par valeur (c'est-à-dire lorsque l'octet est copié), car ils ont la gestion / la propriété de certaines ressources (par exemple une allocation de mémoire ou un descripteur de fichier) et il est très peu probable qu'une copie d'octet duplique correctement cela la possession.
"Eh bien ... qu'est-ce qu'une copie implicite?"
Pensez à un type primitif comme u8
: une copie d'octet est simple, copiez simplement l'octet unique, et une copie sémantique est tout aussi simple, copiez l'octet unique. En particulier, une copie d'octet est une copie sémantique ... Rust a même un trait intégréCopy
qui capture quels types ont des copies sémantiques et d'octets identiques.
Par conséquent, pour ces Copy
types, les utilisations par valeur sont également automatiquement des copies sémantiques, et il est donc parfaitement sûr de continuer à utiliser la source.
let v: u8 = 1;
let w: u8 = v;
println!("{}", v); // perfectly fine
Old : Le NoCopy
marqueur remplace le comportement automatique du compilateur en supposant que les types qui peuvent être Copy
(c'est-à-dire ne contenant que des agrégats de primitives et &
) le sont Copy
. Cependant, cela changera lorsque les caractéristiques intégrées opt-in seront implémentées.
Comme mentionné ci-dessus, les caractéristiques intégrées opt-in sont implémentées, de sorte que le compilateur n'a plus de comportement automatique. Cependant, la règle utilisée pour le comportement automatique dans le passé sont les mêmes règles pour vérifier si sa mise en œuvre est légale Copy
.