rust - ¿Por qué es std:: rc:: Rc<> no Copiar?
smart-pointers (2)
¿Puede alguien explicarme por qué Rc<>
no es Copy
?
Estoy escribiendo código que usa muchos punteros compartidos, y tener que escribir .clone()
todo el tiempo me .clone()
de los nervios.
Me parece que Rc<>
debería consistir simplemente en un puntero, que es un tamaño fijo, por lo que el tipo en sí debería tener el Sized
y, por lo tanto, Copy
, ¿verdad?
¿Me estoy perdiendo de algo?
Me parece que
Rc<>
debería consistir simplemente en un puntero, que es un tamaño fijo, por lo que el tipo en sí debería tener elSized
y, por lo tanto,Copy
, ¿verdad?
Esto no es completamente cierto. Rc
es la abreviatura de R eferencia C onted. Esto significa que el tipo realiza un seguimiento de cuántas referencias apuntan a los datos de propiedad. De esa manera podemos tener múltiples propietarios al mismo tiempo y liberar los datos de forma segura, una vez que el recuento de referencia llegue a 0.
Pero, ¿cómo mantenemos el contador de referencia válido y actualizado? Exactamente, tenemos que hacer algo cada vez que se crea una nueva referencia / propietario y cada vez que se elimina una referencia / propietario. Específicamente, tenemos que aumentar el contador en el primer caso y disminuirlo en el segundo.
El contador disminuye al implementar Drop
, el equivalente de óxido de un destructor. Esta función drop()
se ejecuta cada vez que una variable queda fuera del alcance, perfecta para nuestro objetivo.
Pero cuando hacemos el incremento? Lo has adivinado: en clone()
. El rasgo de Copy
, por definición, dice que un tipo se puede duplicar simplemente copiando bits:
Tipos que se pueden copiar simplemente copiando bits (es decir,
memcpy
).
Esto no es cierto en nuestro caso, porque: sí, simplemente "copiamos bits", ¡pero también hacemos trabajo adicional! ¡Necesitamos incrementar nuestro contador de referencia!
Un tipo no puede implementar Copy
si implementa Drop
( source ). Dado que Rc
lo implementa para disminuir su cuenta de referencia, no es posible.
Además, Rc
no es solo un puntero. Se compone de un Shared
:
pub struct Rc<T: ?Sized> {
ptr: Shared<RcBox<T>>,
}
Lo que, a su vez, no es solo un puntero:
pub struct Shared<T: ?Sized> {
pointer: NonZero<*const T>,
_marker: PhantomData<T>,
}
PhantomData
es necesario para expresar la propiedad de T
:
este marcador no tiene consecuencias por la variación, pero es necesario para que Dropck entienda que, lógicamente, tenemos una
T
Para obtener más información, consulte: https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data