Anuncios

Bienvenidos sean a este post, hoy hablaremos sobre la ultima clave.

Anuncios

Ya hablamos sobre que es la propiedad o ownership y sobre «pedir prestado» o borrowing ahora pasaremos hablar sobre el tercer tema como el ciclo de vida pero que es? Basicamente es el tiempo que una variable permanece en la memoria,

Anuncios

Para ello vamos a crear un nuevo ejemplo al cual llamaremos vida, una vez creado tomaremos el archivo main.rs y modificaremos el codigo generado por el siguiente:

main.rs

fn main() 
{
	let variable: &f32;
	{
		let x = 3.14f32;
		variable = &x;
	}
	println!("variable = {}", variable);
}
Anuncios

Un codigo simple donde primero declaramos una variable luego en un bloque definimos una nueva variable para luego asignar a esta nueva variable a la creada en primera instancia, para finalmente mostrar el nuevo valor de la primera variable, compilemos y veamos su salida:

tinchicus@dbn001vrt:~/lenguajes/rust/refmut/vida$ cargo build
   Compiling vida v0.1.0 (/home/tinchicus/lenguajes/rust/refmut/vida)
error[E0597]: `x` does not live long enough
 --> src/main.rs:6:14
  |
6 |         variable = &x;
  |                    ^^ borrowed value does not live long enough
7 |     }
  |     - `x` dropped here while still borrowed
8 |     println!("variable = {}", variable);
  |                               -------- borrow later used here

For more information about this error, try `rustc --explain E0597`.
error: could not compile `vida` due to previous error
tinchicus@dbn001vrt:~/lenguajes/rust/refmut/vida$
Anuncios

En este caso no se compilo, todo esto es debido a que la variable original al estar fuera del rango del bloque va a vivir mas que x la cual dejara de existir cuando termine el bloque, como el compilador lleva un seguimiento de cada referencia del codigo y fallara su compilacion si una de estas referencias dura mas que su apuntador, si bien este es un codigo simple que sirve como para ver rapidamente un ejemplo de un ciclo de vida esto es solo el comienzo.

Anuncios

Tenemos dos tipos de ciclo de vida, explicito o implicito, veamos un ejemplo de tipo implicitos:

fn funcion(x: f32)
{
	... instrucciones ...
}
Anuncios

Como pueden ver es la tipica instruccion o funcion que hemos visto hasta este momento no tiene nada que no hayamos visto donde su ciclo de vida sera delimitado por la existencia del mismo mientras se ejecute las instrucciones, ahora veamos uno de tipo explicito:

fn funcion<'a>(x: &'a f32)
{
	... instrucciones ...
}
Anuncios

Aqui tenemos una funcion que incluye un generico, tema que hablaremos en otro post mas adelante, y observen que le pusimos un apostrofo (‘) siendo este es el operador de ciclo de vida, el cual no modificara los ciclos de vida de nuestro codigo pero si le dira que mantenga las referencias con vida mientras exista este generico, y para que siga funcionando debemos seguir usando este operador en todos los lugares que este el generico.

Anuncios

Los ciclos de vida son vistos comunmente expresados como struct o impl, de esto hablaremos en otro post mas adelante, pero vamos a centrarnos en struct del cual hablamos en este post, en este mencionamos que son tipos muy particulares y a su vez pueden tener multiples tipos internamente y a su vez podemos extenderlos con todos los parametros que necesitemos, vamos a considerar el siguiente ejemplo:

struct estructura
{
	a: i32,
	b: f32,
	c: bool,
}
Anuncios

Aqui tenemos una estructura simple con tres propiedades con sus respectivas identificaciones y cada una con un tipo diferente, como vinimos viendo hasta ahora mientras una instancia de este struct este dentro del rango o scope podremos acceder a los elementos de esta, pero si queremos que este struct tenga un ciclo de vida variable debemos hacer que el struct tome explicitamente esto y lo asigne a un elemento, veamos un ejemplo:

struct <'a> estructura
{
	cdvvar: &'a f32,
	varn: i32,
}
Anuncios

Pasando la variable de ciclo de vida a uno de los elementos nos asegura que la struct no puede sobrevivir a la referencia que pasamos, tambien podemos usar varios de estos tipos de variables en funciones, veamos un ejemplo:

fn miFuncion <'a>(x: &'a i32, y: &'a i32) -> &'a i32
{
	... instrucciones y debe devolver un valor de i32 ...
}
Anuncios

En este caso utilizamos al generico en dos variables y al devolverlo tambien debemos pasarlo pero tambien podemos hacer lo siguiente:

fn miFuncion <'a, 'b>(x: &'a i32, y: &'b f32)
{
	... instrucciones ...
}
Anuncios

Como pueden ver pueden usar todos los genericos que sean necesarios, para despues usarlo con distintos tipos en nuestra funcion, por ultimo no debemos olvidar el tema del rango o scope donde trabajaremos, para entenderlo vamos a tomar el ejemplo que creamos al principio y vamos a modificar el codigo de main.rs con el siguiente:

main.rs

struct Estructura<'t>
{
	a: &'t i32,
}

fn main() 
{
	let x;
	{
		let y = &5;
		let f = Estructura { a: y };
		x = &f.a;
	}
	println!("{}", x); 
}
Anuncios

Aqui tenemos una struct que tiene un generico con el operador de ciclo de vida, luego en esta tenemos una variable de tipo i32 que tendra este operador, los siguiente sera pasar a la funcion main donde crearemos una variable llamada x, un bloque donde tendremos otra variable con un valor de tipo referencia:

let y = &5;
Anuncios

La cual equivale a:

let y = &y;
Anuncios

Despues crearemos un objeto del struct anterior al cual le pasaremos el valor de y para despues asignar el valor de la propiedad de este objeto (a) a la variable que creamos por fuera del bloque, por ultimo le pedimos que muestre el nuevo valor de x, compilemos y veamos su salida:

tinchicus@dbn001vrt:~/lenguajes/rust/refmut/vida$ cargo build
   Compiling vida v0.1.0 (/home/tinchicus/lenguajes/rust/refmut/vida)
error[E0597]: `f.a` does not live long enough
  --> src/main.rs:12:7
   |
12 |         x = &f.a;
   |             ^^^^ borrowed value does not live long enough
13 |     }
   |     - `f.a` dropped here while still borrowed
14 |     println!("{}", x); 
   |                    - borrow later used here

For more information about this error, try `rustc --explain E0597`.
error: could not compile `vida` due to previous error
tinchicus@dbn001vrt:~/lenguajes/rust/refmut/vida$
Anuncios

Como hasta ahora hemos visto todo es debido al rango, en este caso como la variable y se crea antes que f, en un momento quedara fuera del rango por lo tanto el compilador no puede asegurar que siga existiendo por lo tanto nos infoma que no vive lo suficiente pero si quitamos las llaves este se soluciona porque estara dentro del rango, aunque es menospreciado por muchos desarrolladores algunas veces es muy util tener una variable que exista todo el tiempo y pueda ser utilizada por todos, mas conocidas como globales pero una opcion que podemos usar es static con el operador de ciclo de vida, para ello del codigo anterior tomemos la siguiente linea:

let x;
Anuncios

Y modifiquemos la misma de la siguiente manera:

let x: &'static i32;
Anuncios

Basicamente lo que estamos haciendo con esto es estirar el ciclo de vida de la variable por lo tanto ahora no tendremos el dilema de cuanto vive una variable y como nos afectaba con las propiedades o el «borrow», por ultimo podemos decir que:

  • Cada ciclo de vida en el argumento de la función se convierte en un parámetro de ciclo de vida distinto
  • Si hay un ciclo de vida de entrada, el ciclo de vida se asigna a todos los ciclos de vida en el valor de retorno
Anuncios

En resumen, hoy hemos visto ciclo de vida (lifetime), como es, como nos afecta, como lo interpreta el compilador para evitar errores de referencia o apuntadores huerfanos, tambien hemos visto como es su operador y como nos puede ayudar en algunos momentos pero como esto en realidad no afecta a nuestro codigo, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios
pp258

Donación

Es para mantenimento del sitio, gracias!

$1.50