Nota del editor : esta pregunta se hizo antes de Rust 1.0 y algunas de las afirmaciones en la pregunta no son necesariamente ciertas en Rust 1.0. Algunas respuestas se han actualizado para abordar ambas versiones.
Tengo esta estructura
struct Triplet {
one: i32,
two: i32,
three: i32,
}
Si paso esto a una función, se copia implícitamente. Ahora, a veces leo que algunos valores no se pueden copiar y, por lo tanto, deben moverse.
¿Sería posible hacer que esta estructura Triplet
no se pueda copiar? Por ejemplo, ¿sería posible implementar un rasgo que lo hiciera Triplet
no copiable y por lo tanto "movible"?
Leí en alguna parte que uno tiene que implementar el Clone
rasgo para copiar cosas que no se pueden copiar implícitamente, pero nunca leí al revés, es decir, tener algo que se puede copiar implícitamente y hacerlo no copiable para que se mueva en su lugar.
¿Eso tiene algún sentido?
Respuestas:
Prefacio : esta respuesta se escribió antes de que se implementaran los rasgos incorporados opcionales, específicamente los
Copy
aspectos . He usado comillas en bloque para indicar las secciones que solo se aplicaban al esquema anterior (el que se aplicaba cuando se hizo la pregunta).Los tipos ahora se mueven de forma predeterminada, es decir, cuando define un nuevo tipo, no se implementa a
Copy
menos que lo implemente explícitamente para su tipo:La implementación solo puede existir si todos los tipos contenidos en el nuevo
struct
oenum
son ellos mismosCopy
. De lo contrario, el compilador imprimirá un mensaje de error. También puede existir solo si el tipo no tiene unaDrop
implementación.Para responder a la pregunta que no hiciste ... "¿qué pasa con los movimientos y la copia?":
En primer lugar, definiré dos "copias" diferentes:
(&usize, u64)
, son 16 bytes en una computadora de 64 bits, y una copia superficial sería tomar esos 16 bytes y replicar sus valor en algún otro bloque de memoria de 16 bytes, sin tocar elusize
en el otro extremo del&
. Es decir, equivale a llamarmemcpy
.Rc<T>
implica simplemente aumentar el recuento de referencias, y una copia semántica de aVec<T>
implica crear una nueva asignación y luego copiar semánticamente cada elemento almacenado del antiguo al nuevo. Pueden ser copias profundas (pVec<T>
. Ej. ) O superficiales (p. Ej.Rc<T>
, No toca lo almacenadoT
),Clone
se define vagamente como la menor cantidad de trabajo necesaria para copiar semánticamente un valor de tipoT
desde dentro de&T
aT
.Rust es como C, cada uso por valor de un valor es una copia de byte:
Son copias de bytes ya sea que
T
mueven como son "copiables implícitamente". (Para ser claros, no son necesariamente copias literalmente byte por byte en tiempo de ejecución: el compilador es libre de optimizar las copias si se conserva el comportamiento del código).Sin embargo, hay un problema fundamental con las copias de bytes: terminas con valores duplicados en la memoria, lo que puede ser muy malo si tienen destructores, p. Ej.
Si
w
fuera solo una copia de byte simple,v
habría dos vectores apuntando a la misma asignación, ambos con destructores que lo liberan ... causando un doble libre , lo cual es un problema. NÓTESE BIEN. Esto estaría perfectamente bien, si hiciéramos una copia semántica dev
intow
, ya que entoncesw
serían independientesVec<u8>
y los destructores no se pisotearían entre sí.Aquí hay algunas posibles correcciones:
w
tenga su propia asignación, como C ++ con sus constructores de copia.v
ya no se puede usar y no se ejecuta su destructor.Lo último es lo que hace Rust: un movimiento es solo un uso por valor donde la fuente está invalidada estáticamente, por lo que el compilador evita el uso posterior de la memoria ahora no válida.
Los tipos que tienen destructores deben moverse cuando se usan por valor (también conocido como cuando se copian bytes), ya que tienen administración / propiedad de algún recurso (por ejemplo, una asignación de memoria o un identificador de archivo) y es muy poco probable que una copia de bytes duplique correctamente esto propiedad.
"Bueno ... ¿qué es una copia implícita?"
Piense en un tipo primitivo como
u8
: una copia de byte es simple, simplemente copie el byte único, y una copia semántica es igual de simple, copie el byte único. En particular, una copia de bytes es una copia semántica ... Rust incluso tiene un rasgo incorporadoCopy
que captura qué tipos tienen copias semánticas y de bytes idénticas.Por lo tanto, para estos
Copy
tipos, los usos por valor también son copias semánticas automáticamente, por lo que es perfectamente seguro continuar usando la fuente.Como se mencionó anteriormente, se implementan rasgos incorporados opt-in, por lo que el compilador ya no tiene comportamiento automático. Sin embargo, la regla utilizada para el comportamiento automático en el pasado son las mismas reglas para verificar si su implementación es legal
Copy
.fuente
La forma más sencilla es incrustar algo en su tipo que no se pueda copiar.
La biblioteca estándar proporciona un "tipo de marcador" exactamente para este caso de uso: NoCopy . Por ejemplo:
fuente