Leer y escribir JSON en Rust con Serde

JSON es un formato muy popular para guardar y transmitir información. En Rust podemos leer y escribir JSON de forma transparente gracias a Serde. Serde es una librería para Rust, que permite transformar las structs nativas de Rust en ficheros JSON, YAML, BSON, MsgPack, XML y viceversa. Serde está compuesto de varios plugins, uno para cada formato de archivo, así que vamos a necesitar el plugin de JSON. Es lo que se dice serializar (guardar) y deserializar (leer).

Deserializando JSON con Serde

Para deserializar necesitamos primero definir el struct en Rust que se corresponde al JSON que vamos a cargar. Además, hay que incluir una cláusula derive e indicar que derivamos Deserializable.

Una vez hecho eso podemos realizar la transformación con Serde. Serde admite varios métodos de entrada. En mi caso voy a usar from_reader, que permite transformar desde cualquier std::io::Read. También se podría usar from_str, que permite leer desde un String.

Así pues un programa que lea Landmarks de un fichero JSON como este:

Quedaría así:

Nótese como usamos Vec<Landmark> para indicar que vamos a cargar un JSON que es un array de Landmarks.

Serializar JSON

Ahora si queremos generar un archivo JSON es muy sencillo. Simplemente marcamos Serialize en la misma estructura que queramos serializar. Posteriormente en Serde podemos utilizar diferentes métodos, como to_writer o to_string.

Personalizando la serialización y la deserialización

Serde permite a través de atributos, definir de forma precisa que ocurre con los elementos. En caso de no poner nada, por ejemplo, Serde usará los nombres del struct de Rust en los ficheros y si hay elementos de más, en el fichero al leer, los ignorará.

Por ejemplo, si queremos que en el fichero JSON, en vez de tener name sea nombre, podemos usar rename.

Podemos también no querer guardar algún elemento del struct, usamos skip.


Si queremos que Serde falle en caso de que en el JSON haya más campos de los que hemos definido, usamos deny_unkown_fields.

Y si queremos que el nombre del struct sea distinto al que crea oportuno Serde, podemos redefinirlo también:

Por último, mencionar que Serde permite serializar/deserializar cosas más complejas, con otros structs, con vectores, con HashMap, entre sus elementos,… Lo único que tendrá que pasar es que esas estructuras a su vez sean serializables/deserializables con Serde (es decir, hayan puesto derive(Serialize,Deserialize)).

¡Feliz navidad y próspero 2018! (con fractales)

Feliz Navidad! Si estás leyendo esto, darte las gracias por seguir leyendo este blog. Me alegro mucho de tener cada vez más lectores y es algo que me anima a escribir más y más. En este especial navideño disfrutaremos de éxitos musicales de la navidad, veremos como hacer un fractal muy navideño en Rust y os pediré vuestra opinión.

Comenzamos con In Dulci Jubilo y este cover de Giuliano Ferrace Leanza

El fractal de Koch

Helge von Koch fue un matemático sueco que en 1904 publica un artículo sobre la que desde entonces se llamaría curva de Koch. Se trata de una curva, generada con unas reglas muy simples. La idea fundamental es que se parte de una recta, que se divide en tres trozos iguales. El trozo del medio se sustituye por un triángulo equilátero. Entonces la curva recorre el primer trozo, sube el triángulo, baja el triángulo y continúa recto.

Si ahora en cada uno de los cuatro trozos rectos aplicamos la misma curva de Koch obtenemos esto:

Según la terminología de Mandelbrot, este fractal es un teragón. Si ahora en vez de aplicar esto sobre una recta, lo hacemos sobre un triángulo equilátero con sus tres trozos rectos iguales, ¿qué obtenemos?

Turtle, una librería de Rust para gráficos tortuga

Quizá entre mis lectores haya alguno que aprendió a programar con LOGO. Una de las características de LOGO era que disponía de una tortuga con la que íbamos dibujando según nos movíamos. Esta manera de dibujar, no es óptima en rendimiento puro, pero es ideal para fractales y para enseñar a programar. Python incorpora en su librería estándar el módulo turtle y en Rust está a punto de salir una librería que soporta una API similar llamada turtle.

Lo primero que tenemos que hacer es definir la curva de Koch con turtle.

Con esto nos valdría, pero no es recursivo. Necesitamos poder aplicar la curva de Koch en nuestras rectas (donde hacemos forward) de forma recursiva. Pero si lo hacemos recursivo de forma infinita no acabará nunca, es por ello que tenemos que indicar cuantos niveles de recursividad queremos.

Esto ya tiene más sentido. Ahora juntemos todo lo necesario:

Si compilamos y ejecutamos esto con Cargo:

Y aquí tenemos al copo de nieve de Koch.

Tenéis el proyecto completo en GitHub: https://github.com/aarroyoc/fractal_koch_rust

Fractal (bis)

Otro fractal muy interesante que programé mientras estaba haciendo el copo de nieve fue este otro. Desconozco si tiene nombre. Es algo más complejo de implementar y lo de los colores me dio bastantes dolores de cabeza hasta que encontré una progresión bella.

Su código es (en este caso Python):

 

Vuestra opinión

Ahora os pido que me déis vuestra opinión sobre el blog. Usad los comentarios de debajo y contadme: ¿Cuál es el mayor problema del blog?, ¿crees que los contenidos salen con frecuencia suficiente y necesaria?, ¿los temas son interesantes?, ¿me voy mucho por las ramas?, … Todo esto lo tendré en cuenta de cara al año que viene.

Aprovecho para recordar que podéis suscribiros a la lista de correo para que os llegue un nuevo correo con cada post, podeís suscribiros al blog por RSS, hay un canal de Telegram y una página en Facebook. En Twitter, GNU Social e Instagram solo tengo perfiles personales, pero si os interesa, allí estoy. Por último en Google+ todavía sigo mandando los artículos.

Si os parece que me merezco una caña, siempre puedes donar usando PayPal, criptodivisas, tu apartamento en Comillas, …

Sin más, feliz próspero año 2018

 

Diversión con punteros en Rust: bloques unsafe

Hola, soy Adrián Arroyo y bienvenidos a un nuevo episodio de Diversión con Punteros.

Hoy vamos a hablar de un tema apasionante. Los bloques unsafe de Rust así como de los raw pointers. ¿Has programado en C? Si es así, los raw pointers de Rust son exactamente iguales a los punteros de C. Si no sabes lo que es un puntero, te lo explico.

¿Qué es un puntero?

Un puntero es un tipo de variable que en vez de almacenar el dato, almacena la posición en memoria donde se encuentra el dato.

En lenguajes en lo que todo es un objeto (como Python), nunca trabajamos con los datos reales, sino siempre con punteros, pero el lenguaje lo gestiona de forma automática. En lenguajes más cercanos al metal por contra sí que suele dejarse esta opción.

Nuestro puntero es la variable que contiene 0x00ffbea0 y que apunta a la dirección de memoria donde se encuentra el dato

Rust tiene distintos tipos de punteros: Box, Rc, Arc, Vec, … Estos punteros son transparentes al usuario y muchas veces no tenemos que preocuparnos de su funcionamiento. Sin embargo, muchas veces queremos tener un control más fino del ordenador. Esto lo lograremos con los raw pointers. Se trata de punteros con los que podemos operar y desreferenciar.

Crear raw pointers no supone ningún problema, pero acceder al valor al que apuntan en memoria sí. Podría darse el caso de que no existiera valor alguno o hubiese sido modificado. En los punteros normales, el compilador de Rust se encarga de que no ocurra, pero en los raw pointers el compilador no lo puede saber. Es por ello, que para acceder al valor de un raw_pointer necesitas usar bloques de código unsafe, código inseguro en Rust.

Creando un raw pointer

Lo primero que hay que saber es que existen dos tipos de raw pointers en Rust, los mutables y los inmutables.

Los punteros inmutables tienen el tipo *const T y los mutables el tipo *mut T.

 

En este ejemplo, creamos una variable con valor 5 y le creamos un puntero, que contiene la dirección de memoria donde está el dato. Para representar la dirección de memoria se suele usar la notación hexadecimal. Antes debemos hacer un cast a usize. usize es un tipo en Rust cuyo tamaño depende de la máquina en cuestión (32 bits en máquinas de 32 bits, 64 bits en máquinas de 64 bits), siendo usado para representar direcciones de memoria, puesto que tiene el tamaño exacto para almacenarlas.

Hasta ahora no hemos usado unsafe. Esto es porque no hemos probado a acceder al valor. Para acceder a un valor, o deferrenciar, usamos el operador *.

Ambos prints imprimen 5. Hasta aquí no hemos hecho nada interesante con punteros. Todo esto era más fácil hacerlo sin punteros. Veamos alguna aplicación práctica de los punteros.

Modificar datos sin control

Si te pongo este código, ¿me puedes decir que salida dará?

Uno podría pensar que como en ningún sitio reasignamos numero, y numero es una variable de tipo i32, que implementa Copy, es imposible modificarle el valor. Y eso es correcto en las reglas de Rust normales, pero en unsafe, podemos pasar el puntero hacia otras funciones (los punteros también son Copy, ocupan el tamaño de un usize). Y esas funciones pueden modificar los datos en memoria a su antojo. Así, pues, la respuesta correcta es indeterminado. Hacer esto es una mala práctica, pero en ocasiones se puede ganar rendimiento o interactuar con una librería de C usando estos métodos.

Esta sería la versión completa del programa.

Aritmética de punteros

Una vez tenemos acceso a memoria podemos acceder a cualquier parte de memoria (en sistemas operativos modernos, memoria que esté asignada a nuestro programa). En C simplemente podíamos operar con el puntero como si fuese un número, con sumas, restas, multiplicaciones y divisiones. Estas operaciones eran un poco traicioneras porque eran relativas a la máquina. Sumar 1 a un puntero de int equivalía en realidad a sumar 4 al puntero en una máquina de 32 bits. En Rust esto no se permite, pero a cambio tenemos métodos que nos permiten hacer lo mismo. El más importante es offset. El offset nos permite desplazarnos por la memoria hacia delante y hacia atrás.

Este programa parte de una suposición para funcionar. Y es que numero, b y c están contiguos en memoria y en el mismo orden que como los que he declarado. En el puntero tenemos la dirección a numero, es decir, a 5. Sin embargo, si avanzamos en la memoria una posición llegaremos a al 35, y si avanzamos dos, llegamos a 42. Entonces podemos editar el contenido de esa memoria. Al acabar el programa b vale 120. Hemos modificado el valor y ni siquiera b se había declarado como mut. Esto os recuerdo, usadlo solo en casos excepcionales.

Reservar memoria al estilo C

Estas cosas empiezan a tener utilidad en cuanto podemos usar memoria dinámica al estilo C, es decir, con malloc, free, calloc y compañía. El equivalente a malloc en Rust suele ser Box o Vec y es lo que debemos usar. Box sabe que espacio en memoria tiene que reservar de antemano y Vec ya está preparado para ir creciendo de forma segura.

En este caso usamos malloc como en C para generar un array de forma dinámica con espacio suficiente para almacenar 10 elementos de tamaño i32.

Con esto ya hemos visto el lado oscuro de Rust, la parte unsafe. No hemos visto como llamar a funciones de C directamente, algo que también require usar bloques unsafe.

Como vemos, Rust no nos limita a la hora de hacer cualquier cosa que queramos, solo que nos reduce a los bloques unsafe, para que nosotros mismos tengamos mejor control de lo que hagamos.

Cheatsheet de contenedores de Rust

Raph Levien de Google ha publicado la siguiente infografía o cheatsheet donde podemos ver como funciona cada contenedor de datos en Rust. Es un recurso muy interesante bajo licencia Creative Commons 4.0 BY, que si bien no es necesario para poder programar en Rust, puede sernos de utilidad cuando trabajemos a bajo nivel con el lenguaje.

Enlace original

Crear ventanas y botones en Rust con GTK

Cuando salió el Macintosh allá por 1984, fue novedosa su interfaz gráfica. En efecto, fue la primera interfaz gráfica popular y ha servido de inspiración para muchos otros sistemas de ventanas.

Hoy vamos a introducir una manera de crear interfaces gráficas de usuario (GUI) con Rust. Para ello usaremos GTK. GTK funciona sobre GNU/Linux, macOS y Windows (aunque es un poco más complicado de lo que debiera).

Para ello usaremos la fantástica crate gtk del proyecto Gtk-rs.

Instalando gtk

Añade al fichero Cargo.toml las siguiente líneas:

Y ejecuta cargo build.

Creando una ventana

Lo primero que vamos a hacer es crear una ventana.

Importamos gtk e iniciamos GTK.

Ahora podemos crear una ventana. GtkWindow es un struct con métodos asociados, por lo que podemos ajustar ciertos parámetros. En este código parece que estamos usando orientación a objetos, pero recuerdo que Rust técnicamente no tiene clases ni herencia.

Ahora vamos a añadir un evento para que cuando se pulse la X en nuestra ventana, se cierre el programa. Para ello tenemos que entender como funciona GTK. Cuando programamos en GTK lo que hacemos es configurar la aplicación y posteriormente ceder la ejecución a una función gtk_main que procesa todos los eventos según haya sido configurado. Para configurar los eventos usaremos callbacks, que en Rust se pueden implementar con closures. Las funciones que en GTK nos permiten conectar eventos siempre tienen el prefijo connect.

Ahora vamos a mostrar la ventana. Por defecto en GTK todos los widgets (todo lo que se muestra en pantalla) está oculto. Para mostrar todos los widgets que se han añadido a la ventana se suele usar show_all, que va a haciendo show de forma recursiva. Por último, le damos el control de la aplicación a gtk::main.

Una vez hecho esto, si compilamos con cargo run ya deberíamos ver una preciosa ventana GTK.

Y por supuesto, si pulsamos la X, la aplicación se cierra.

Layouts y botones en GTK

Vamos ahora a añadir dos cosas: un botón y un label que nos de un número del dado. Para poner varios elementos en una aplicación GTK es recomendable usar un layout. Voy a usar el layout Box, que permite agrupar los widgets de forma vertical u horizontal. Para los números aleatorios voy a usar la crate rand. El código final sería así:

Aquí pasan varias cosas interesantes. La primera es que usamos add para ir añadiendo de forma jerárquica los widgets. Debajo de Window está Box y debajo de Box tenemos Button y Label.

Por otra parte, vemos que la función asociada al evento del click de los botones es connect_clicked. Bien, en este ejemplo he introducido algo importante. Estoy modificando un widget desde un evento relacionado a otro widget. ¿Esto como se lleva con las reglas de propiedad/ownership de Rust? Bastante mal. Rust no puede saber si cuando se ejecuta el evento tenemos acceso al widget en cuestión que vamos a modificar. Afortunadamente, la API de gtk-rs ha sido diseñada con esto en cuenta y simplemente podemos hacer una llamada a clone para obtener otra referencia al objeto, que podemos pasar al closure (con move). Este clonado no lo es tal, sino que hace uso de Rc. Simplemente se nos presenta de forma transparente.

El ejemplo final: dibujando con Cairo

En este último ejemplo voy a añadir un widget donde se podrá ver la cara del dado que ha salido. Para ello uso un GtkDrawingArea, que permite usar la API de Cairo.

Con esto tenemos lo básico para empezar a diseñar GUIs con GTK y Rust.