Los primeros meses de un alborotador novato generalmente se reducen a un encabezado sobre el concepto de vida y posesión. Algunas personas analizan esto, pero para aquellos que pudieron sobrevivir, esto ya no parece inusual o incorrecto. Describiré los puntos clave que, me parece, han ayudado a adaptarse más rápido y mejor al concepto de vida y posesiones. 
Por supuesto, el boletín oficial es más completo y más detallado, pero también requiere más tiempo y paciencia para comprender y absorber completamente toda la información. Traté de evitar una gran cantidad de detalles y presentar todo en orden creciente de complejidad, en un intento de hacer que este artículo sea más accesible para aquellos que comenzaron a mirar el rastreo o no entendieron realmente los momentos iniciales del tablero de anuncios oficial.
También me hizo escribir que, por ejemplo, de las mónadas, puedes encontrar algunos materiales de capacitación oficiales, pero no siempre se comprenden bien, y la comprensión surge solo después de leer algo como "otra introducción" sobre este tema.
Toda la vida
Primero tenemos que sentirnos cómodos con dos cosas: el final del bloque y mover el valor a otro bloque. Más adelante comenzaremos a complicarlo agregando "préstamos", "mutabilidad" y "mutabilidad oculta".
En primer lugar, la vida de un valor está determinada por el siguiente segmento:
- El comienzo de la vida: crear valor. Esto es común para la mayoría de los lenguajes de programación, por lo que no lleva ninguna carga inusual.
- El fin de la vida. Aquí es donde Rust llamará automáticamente al destructor y se olvidará del valor. En un bloque de alcance, esto sucederá al final de este bloque sin moverse. El seguimiento mental del final de la vida es, en mi opinión, la clave para una interacción exitosa con el prestatario.
Agregaré un detalle que puede ser útil: si hay varios valores en el alcance, se destruirán en el orden inverso de la creación.
Otro punto: crearé una cadena, porque no tiene un marcador Copiar, y los valores que tienen este marcador no se mueven sino que se copian, lo que se considera una operación bastante barata, pero cambia el comportamiento del movimiento (y hace que sea más fácil trabajar con tipos primitivos), pero más sobre eso más tarde.
Los ejemplos se pueden ejecutar aquí: https://play.rust-lang.org/
fn main() { {
Con un bloque simple, todo es relativamente simple, la siguiente etapa ocurre cuando usamos cosas aparentemente simples como funciones y cierres:
En movimiento
Agregue un concepto como mover un valor. En palabras simples, "mover" significa que el bloque actual ya no está interesado en el destino del valor y lo olvida, y su destino se transfiere a otro bloque, por ejemplo, a otra función, a un cierre, o simplemente a otro valor.
fn f<T: std::fmt::Display>(x: T) {
Con cierres.
Para que el cierre mueva el valor capturado a su bloque, se usa la palabra clave move, si no escribe move, el valor es prestado, sobre lo que escribiré muy pronto.
fn main() { let a = "a".to_string();
Puede mover tanto a la función como a la función u otro valor.
Este ejemplo demuestra cómo hacer un seguimiento de las formas en que se mueven los valores para vivir en paz con el prestatario.
fn f(x: String) -> String { x + " and x"
Préstamos
Introducimos este nuevo concepto: a diferencia del movimiento, esto significa que el bloque actual se reserva el control del valor, simplemente permite que el otro bloque use su valor.
Observo que los préstamos también tienen lugar donde terminaron, lo que no es muy importante en estos ejemplos, pero aparecerá en el próximo párrafo.
Nota: No escribiré sobre cómo especificar la vida útil directamente en la función, ya que el óxido moderno lo hace automáticamente mejor que en los viejos tiempos, y la divulgación de todo esto es unas pocas páginas más.
fn f(x: &String) {
Con cierres similares:
fn main() { let mut a = "a".to_string();
En realidad, en la mayoría de estas construcciones simples, el usuario solo necesita decidir dónde quiere terminar la vida del valor: al final del bloque actual y prestarlo a algunas funciones, o, si sabemos que ya no necesitamos el valor, luego moverlo a la función al final por el cual será destruido, cuanto más rápido liberemos la memoria, pero el valor ya no estará disponible en el bloque actual.
Mutabilidad
En rasta, como, por ejemplo, en kotlin, hay una división en valores mutables y no estables. Pero surge el problema de que la mutabilidad tiene un efecto en los préstamos:
Puede pedir prestado un valor no estable muchas veces, y un valor mutable se puede pedir prestado mutuamente solo una vez. No puede mutar un valor prestado anteriormente.
Un ejemplo que no está relacionado con los anteriores, cuando este concepto nos salva de problemas al prohibir los préstamos mutables y no estables simultáneos:
fn main() { let mut a = "abc".to_string(); for x in a.chars() {
Aquí ya es necesario abastecerse de varios trucos para satisfacer, en su mayor parte, los justos reclamos de la rasta. En el ejemplo anterior, lo más fácil sería clonar "a" -> el clon tendrá un préstamo no estable y no se relacionará con el "a" original.
for x in a.clone().chars() {
Pero es mejor volver a nuestros ejemplos para mantener la coherencia. Necesitamos cambiar "a" y no podemos hacerlo.
fn main() { let mut a = "a".to_string();
Mutación oculta
Teóricamente, se puede pasar un cierre a alguna función que procesa, por ejemplo, de forma asíncrona en otro subproceso, y entonces realmente tendríamos problemas, pero en este caso, el revisor de préstamos está reasegurado, aunque esto no cancela el hecho de que de alguna manera debemos estar de acuerdo .
En pocas palabras: necesitamos dos préstamos mutantes, pero el rastreo solo permite una cosa, pero los astutos inventores del rasta tienen una "mutación oculta": RefCell.
RefCell - lo que envolvemos en RefCell - el ráster lo considera nemable, sin embargo, al usar la función préstamos_mut () podemos extraer temporalmente un enlace mutable por el cual puede cambiar el valor, pero hay un matiz importante : el enlace solo se puede obtener cuando RefCell en tiempo de ejecución se asegura de que no haya otros préstamos activos, de lo contrario lanzará pánico o devolverá un error si se usa try_borrow_mut (). Es decir aquí el crecimiento genera todas las preocupaciones sobre los préstamos al cuidado del usuario, y él mismo debe asegurarse de no tomar prestado el valor de varios lugares a la vez.
use std::cell::RefCell; fn main() { let a = RefCell::new("a".to_string());
Rc Link Counter
Esta construcción es familiar en muchos idiomas, y se usa en rast, cuando, por ejemplo, no podemos tomar prestado un valor por alguna razón, y es necesario tener varios valores de referencia para un solo valor. Rc, como su nombre lo indica, es simplemente un contador de referencia que posee un valor, puede tomar prestados enlaces no confiables, contar su número y tan pronto como se restablece su número, destruye el valor y a sí mismo. Resulta que Rc permite, por así decirlo, expandir en secreto la vida útil del valor que contiene.
Agregaré que el rastreo puede eliminar automáticamente las estructuras para las que está definido, lo que significa que para trabajar con Rc, por regla general, no necesita ninguna extracción adicional del valor interno y solo trabajamos con Rc como con el valor dentro de él.
Aquí, un ejemplo simple se pensó un poco, intentemos emular que el cierre del ejemplo anterior no quiere aceptar & T o & String, pero solo quiere String:
fn f(x: String) {
Este problema se resolvería fácilmente si pudiéramos cambiar la función a fn f(x: &String)
(o & str), pero imaginemos que por alguna razón no podemos usar &
Utilizamos Rc
use std::rc::Rc; fn f(x: Rc<String>) {
Agregaré el último ejemplo, ya que uno de los pares de contenedores más frecuentes que se pueden encontrar es Rc <RefCell>
use std::rc::Rc; use std::cell::RefCell; fn f(x: Rc<RefCell<String>>) { x.borrow_mut().push_str(" and x");
Además, sería lógico mover este tutorial a un análogo de Rc-Arc seguro para subprocesos y luego continuar con Mutex, pero no hablará sobre seguridad de subprocesos y comprobación de préstamos en un párrafo, y no está claro si este tipo de artículo es necesario, ya que hay un subproceso oficial. Entonces concluyo.