Bindings entre Rust y C/C++ con bindgen

Rust es un lenguaje con muchas posibilidades pero existe mucho código ya escrito en librerías de C y C++. Código que ha llevado mucho tiempo, que ha sido probado en miles de escenarios, software maduro y que no compensa reescribir. Afortunadamente podemos reutilizar ese código en nuestras aplicaciones Rust a través de bindings. Los bindings no son más que trozos de código que sirven de pegamento entre lenguajes. Esto es algo que se ha podido hacer desde siempre pero dependiendo de la librería podía llegar a ser muy tedioso. Afortunadamente tenemos bindgen, un programa que permite generar estos bindings de forma automática analizando el código de la librería de C o C++.

En este post veremos como usar SQLite desde Rust usando bindgen.

Instalando bindgen

En primer lugar necesitamos tener instalado Clang 3.9 o superior. En Ubuntu o Debian necesitamos estos paquetes:

Para el resto de plataformas puedes descargar el binario desde la página de descargas de LLVM.

Bindgen permite dos modos de uso: línea de comandos o desde el código Rust. El más habitual es desde código Rust pero antes veremos el modo en línea de comandos.

Modo línea de comandos

Para bindings sencillos podemos usar el modo línea de comandos. Instalamos binden con Cargo:

Su uso es muy sencillo:

Simplemente indicamos el fichero de cabecera que queremos traducir y su correspondiente fichero de salida en Rust. Este fichero será el pegamento. Vamos a crear un programa que use este pegamento:

Como se puede apreciar, las llamadas al módulo de pegamento de hacen desde un bloque unsafe ya que se van a usar punteros al estilo C, de forma insegura. Hace tiempo escribí sobre ello así que voy a saltarme esa parte.

Compilamos enlazando de forma manual libsqlite3 de la siguiente forma:

Si todo va bien, compilará aunque con numerosos warnings. En principio no son importantes.

Ahora si ejecutamos el programa resultante debería crear una base de datos nueva con una tabla contacts y los datos insertados.

¡Hemos conseguido llamar a una librería de C desde Rust y no hemos escrito ningún binding!

Build.rs

El sistema anterior funciona, pero no es lo más práctico, además no usa Cargo que es el sistema estándar de construcción de programas y crates un Rust. Lo habitual es dejar este proceso automatizado en el fichero build.rs que se ejecuta con Cargo.

Lo primero es añadir la siguiente línea al fichero Cargo.toml:

El siguiente paso consiste en crear un archivo cabecera de C que a su vez haga referencia a todos los archivos de cabecera que necesitamos. En el caso de SQLite es bastante simple.

Y lo llamamos wrapper.h

Ahora viene lo interesante. Dentro de build.rs creamos un programa que gracias a la API de bindgen haga lo mismo que la línea de comandos.

El archivo build.rs debe estar en la misma carpeta que Cargo.toml para que funcione.

Finalmente para hacer accesible nuestros bindings creamos un módulo llamado sqlite.rs con el siguiente contenido.

Lo único que hace es desactivar unos warnings molestos e incluir el texto de bindings.rs al completo.

Una vez hecho esto podemos usar desde el programa principal la librería de la misma forma que hemos visto antes.

Ahora podríamos usar estos bindings directamente en nuestro programa o rustizarlos (darles una capa segura alrededor e idiomática) y subirlo a Crates.io.

El código del post está en GitHub

Futures y Tokio: programar de forma asíncrona en Rust

Rust es un lenguaje muy potente y uno de sus principales focos de diseño es la concurrencia. Sin embargo, si damos un repaso por la librería estándar veremos un par de cosas nada más, algo que puede resultar decepcionante. Esto se debe a que Rust no impone ningún modelo específico de concurrencia como sí lo hacen Go o Erlang. Rust nos provee de los ladrillos de construcción necesarios.

De forma estándar, Rust nos provee de Mutex, atómicos, threads, variables de condición, RwLock y un modelo algo más avanzado de mensajería mediante canales de múltiples productores y un único consumidor (mpsc). En el exterior tenemos crates como Actix que nos proveen del modelo de actores en Rust (dicen que es similar a Akka, yo lo desconozco), modelos de mensajería por canales más flexibles (mpmc) y una cosa muy interesante llamado Futures. Los futures o futuros no son esos contratos que se realizan en bolsa sobre el valor futuro de una acción, sino que son una manera cómoda de manejar valores que existirán en el futuro. Si has usado JavaScript o C# igual te suenan las Promises o Promesas y los Task respectivamente. Los futuros de Rust son exactamente lo mismo.

¿Qué es un futuro?

Un futuro es una variable que representa un dato que todavía no existe. Tenemos la promesa de que ese valor existirá en el futuro. La ventaja es que podemos usarlo aun cuando todavía no tenga un valor. Esto además permite escribir código asíncrono con facilidad.

Una diferencia con respecto a los Promises de JavaScript es que los futuros se basan en poll en vez de en push. Es decir, los futuros van a ir preguntando si el valor ya está disponible. Ante esta pregunta se puede responder con un error, con todavía no está disponible y con ya está disponible, aquí tienes.

Veamos un ejemplo muy tonto pero que puede servirnos para entender algunas cosas.

Definimos un nuevo futuro, SumFuture, que devuelve el resultado de una suma. El futuro en su función poll duerme el hilo 1 segundo y después devuelve el resultado correcto. En la función main llamamos a suma que devuelve un futuro en vez del resultado. Con el futuro, esperamos con wait a que se resuelva y lo mostramos. Cuando un futuro se ejecuta se convierte en una tarea. Las tareas necesitan ejecutores. Wait ejecuta los futuros en el mismo hilo desde el que se hace la llamada, pero existen otras opciones. El programa, tarda un segundo en imprimir el número.

Pero esto no sirve para nada

Bueno, quizá ahora parezca una tontería, pero en cuanto introduzcamos más elementos, todo tendrá más sentido.

Una característica de los futuros es que se pueden encadenar, tal que así:

Ejecutores

Los futuros nos ayudan con la concurrencia, esto es porque se puede esperar a varios futuros a la vez sin problema. Una manera de hacerlo es con CpuPool, un ejecutor que tiene un pool de hilos ya creados. Su uso es muy sencillo, en este ejemplo vemos como hago dos operaciones en paralelo:

Con spawn añadimos un nuevo futuro a la ejecución y nos devuelve otro futuro. Con join podemos unir dos futuros en uno solo que se resuelva cuando ambos hayan resuelto. Finalmente hacemos wait en el hilo principal para esperar a que las sumas se hagan e imprimir el resultado. Existen otros ejecutores. Con Tokio usaremos otro.

Tokio

Tokio es una librería que nos permite ejecutar código de entrada/salida de forma asíncrona, usando futuros por debajo. Son especialmente útiles, la parte de red de Tokio y sus ejecutores correspondientes.

En este ejemplo tenemos un pequeño servidor TCP que se mantiene a la espera de clientes, imprime un mensaje y cierra la conexión. Este servidor solo utiliza un hilo, no obstante, ya usa futuros. server es un futuro infinito, que nunca acaba, que ejecutamos en el hilo actual con run->spawn. El run solo es necesario para ejecutar el primer futuro, el que mantendrá viva la aplicación. spawn empieza a ejecutar el futuro, que entonces se pasa a denominar tarea. Aquí es Tokio en vez de CpuPool, el planificador de tareas.

Servidor de Echo en Tokio

Ahora vamos a ir con un ejemplo más interesante, ahora el cliente y el servidor estarán conectados durante un tiempo, mandando el cliente un mensaje y el servidor respondiendo. Pero queremos que haya varios clientes simultáneamente. Usando futuros y Tokio podemos hacerlo en un mismo hilo (al estilo Node.js).

¿Qué hacemos ahora? Ahora a cada socket de cliente asignamos una tarea, que se encarga, de forma independiente, de ir leyendo línea a línea (gracias a LineCodec).

A partir de aquí se programa en forma de stream, un patrón muy común en la programación con futuros.

En el primer map imprimimos la línea en el servidor y la seguimos pasando por el stream. El siguiente paso es escribir en writer, con forward imprimimos y mandamos esa línea al cliente. Forward a su vez devuelve una tupla con datos (útil para seguir haciendo cosas). Como no los necesitamos, hacemos un map cuya única finalidad sea descartar los valores y finalmente un map_err para capturar posibles errores. Una vez hecho esto tenemos un futuro listo para ser ejecutado. Iniciamos la tarea con spawn y nos olvidamos, pasando a esperar a por un nuevo cliente.

Ahora, en el servidor podemos manejar varios clientes a la vez, cada uno en una tarea distinta, dentro de un mismo hilo.

Cancelando la tarea

Esta tarea que maneja el cliente es infinita, es decir, no se para. La razón es que reader es un lector que genera un futuro infinito esperando a nuevas líneas. Pero, ¿existe algún método para parar la tarea desde el servidor?

Esto no es algo demasiado trivial, pero se puede hacer de manera sencilla usando canales MPSC de Rust y futuros.

La idea aquí es hacer que de alguna manera, el futuro pueda resolverse y así finalizar la tarea. Una función interesante es select2 que devuelve un futuro fusión de dos futuros. Este futuro se resuelve (devuelve valor y acaba la tarea) cuando alguno de ellos lo haga. Como el futuro de reader nunca acabará, entonces basta con poner un futuro que cuando queramos cerrar la conexión resolveremos.

Este futuro es cancel de tipo Cancellable. No viene en la librería, lo he creado arriba y lo que hace es esperar a que el extremo del canal reciba una comunicación. El valor nos da igual, simplemente que se haya producido un ping. Una vez hecho eso, el futuro resuelve y la conexión se cierra.

En el ejemplo, cuando el cliente manda bye la conexión se cierra.

Y esto es una breve introducción a los futuros y a Tokio en Rust. Todavía queda ver como podemos usar async/await para no tener que escribir todo esto en forma de stream.

Lenguajes de programación que todo buen programador debe conocer

Dice Bjarne Stroustrup (creador de C++) que nadie debería llamarse un profesional si no conoce al menos 5 lenguajes suficientemente diferentes entre sí. Comparto con él esa afirmación, así que he decidido hacer una lista con esos 5 lenguajes suficientemente diferentes entre sí. La razón de que sean diferentes entre sí es que implementan paradigmas distintos.

Paradigmas de programación

Cada lenguaje está moldeado en base a uno o varios paradigmas de programación. Aunque no hay una teoría con la que todos los autores esten de acuerdo, bajo mi punto de vista existen dos grandes grupos de paradigmas de programación. Imperativos y Declarativos. Los imperativos responden a la pregunta de ¿Cómo se calcula esto? y los declarativos ¿Cuál es el resultado de esto?. Otra manera de verlo es ver al paradigma imperativo como un intento de simplificar la electrónica subyacente. El paradigma declarativo por contra muchas veces se origina de la teoría matemática y posteriormente se aplica al ordenador.

Cada uno de estos paradigmas a su vez tienen más sub-paradigmas y luego existen paradigmas transversales, es decir, que se pueden aplicar (o no) tanto en lenguajes imperativos como en lenguajes declarativos.

Un buen programador necesita conocer estos paradigmas.

Prolog

Prolog es un claro ejemplo de programación lógica. Se trata de un lenguaje declarativo. Diseñado en los años 70 en Francia, Prolog tuvo mucha popularidad en el desarrollo de Inteligencia Artificial debido a sus características lógicas. En esencia, Prolog se basa en la demostración de predicados, similares a los del álgebra de predicados.

Ejemplos de lógica de predicados

Un programa Prolog es en realidad un conjunto de afirmaciones o predicados. En tiempo de ejecución se realizan preguntas sobre predicados. Prolog intenta entonces demostrar la veracidad del predicado, para ello usa el mecanismo de backtracing. Una característica muy interesante de Prolog es el pattern matching, que básicamente permite preguntar para qué valor de una variable se cumple un predicado. Esto permite realizar cosas muy interesantes:

Ahora, para saber quién es la madre de Sonia intentamos demostrar:

Y responderá X = veronica.

En predicados sin variables, Prolog solo devuelve true o false.

Existen varios compiladores/intérpretes de Prolog, siendo el más conocido SWI-Prolog, multiplataforma y con una extensa librería que incluye GUI multiplataforma y framework web. También existe GNU Prolog y Visual Prolog (antiguamente conocido como Turbo Prolog), aunque este último no se le considera Prolog auténtico por ser demasiado diferente al resto.

Haskell

Haskell es un lenguaje declarativo que implementa el paradigma funcional. Es uno de los pocos lenguajes funcionales que son 100% puros. Se entiende por puros como la capacidad de no generar efectos colaterales. Haskell es un lenguaje fuertemente tipado y deriva de la teoría de categorías. Haskell ha sido objeto (hasta cierto punto merecido) de muchas bromas sobre este asunto, ya que para la mayoría de programadores, conocer teoría de categorías no es demasiado práctico.

Otra característica de Haskell es que es perezoso, lo que significa que no calculará nada que no sea estrictamente necesario (esto puede parecer muy raro hasta que lo entiendes en la práctica).

Este código pertenece al juego de la vida de Conway que (también) implementé en Haskell, simplemente por curiosidad, ya que no está optimizado. Faltaría el módulo Reader, así que no intentéis compilarlo directamente.

Haskell como tal no tiene variables ni bucles y sus condicionales no son exactamente iguales a los de los lenguajes imperativos (aunque se use if, en el caso de Haskell son bloques que siempre deben devolver un valor).

Aunque siempre ha sido un lenguaje académico (nació en 1994, un año antes que Java), ahora ha alcanzado bastante popularidad y algunas empresas como Facebook lo usan en producción.

El compilador más conocido, capaz de generar código nativo, es GHC. Este dispone de un REPL llamado GHCi y también compila a JavaScript y se está trabajando en WebAssembly. Otro intérprete es Hugs, pero solo es compatible con Haskell98.

La forma recomendada de instalar GHC es con Stack. Usa Stack y ahórrate quebraderos de cabeza.

Otros lenguajes similares a Haskell son ElmPureScriptEta e Idris. Este último compila a JavaScript y pone énfasis en su librería, que es capaz de competir con Angular y React.

Racket (Lisp)

Lisp no puede faltar nunca. Se trata de uno de los primeros lenguajes de programación y sigue siendo tan actual como el primer día, gracias a su simple pero efectivo diseño, inspirado en el cálculo lambda de Alonzo Church. No obstante, Lisp ha evolucionado mucho, si me preguntan que dialecto/lenguaje de Lisp merece la pena aprender ahora diría Racket. Racket es un lenguaje de programación funcional y programación orientada a objetos. Racket no es 100% puro como Haskell (la mayoría de dialectos de Lisp no lo son) pero sigue siendo muy interesante. También, tiene tipado débil en contraposición al tipado fuerte de Haskell.

Racket desciende a su vez de Scheme, que es una de los ramas principales de Lisp, siendo la otra Common Lisp. ¿Por qué estas diferencias? La gente de Scheme prefiere un lenguaje elegante, lo más funcional posible mientras que la gente de Common Lisp prefirieron sacrificar eso a cambio de un lenguaje más práctico en el mundo real.

Racket cuenta con una extensísima librería estándar capaz de realizar todo lo que te imagines sin gran problema. Racket también soporta las famosas y potentes macros.

Triángulo de Sierpinski en el IDE DrRacket.

JavaScript

JavaScript está en todas partes. Es uno de los lenguajes con mayor aplicación práctica. Web, servidor, bases de datos, scripting, plugins e incluso IoT. Por eso JavaScript me parece un lenguaje que deba estar en esta lista. Y sin embargo no es fácil categorizarlo correctamente. Ante todo, estamos ante un lenguaje de programación imperativo, con orientación a objetos y buen soporte a la orientación a eventos.

Y antes de que se me venga alguien a comerme, sí, JavaScript está orientado a objetos, aunque no siguen el patrón de clase/herencia que C++ y Java tienen tan acostumbrados a la gente. La orientación a objetos por prototipos no la inventó JavaScript, sino que ya estaba en otros lenguajes como Self y más actualmente Io. Y realmente lenguajes como Python o Ruby no se alejan mucho de esto internamente.

Actualmente, con la versión ES7, tenemos muchas cosas interesantes en programación asíncrona y clases al estilo Java que no son más que azúcar sintáctico sobre el verdadero modelo de JS.

Definitivamente, JavaScript es un lenguaje muy interesante y aunque a algunas personas les pueda parecer un caos, ciertamente es muy productivo y útil. Aprovechar al máximo JavaScript requiere pensar de forma distinta a como lo harías en otros lenguajes imperativos.

C# (o Java)

C# es otro lenguaje bastante complejo, imperativo, orientado a objetos por clases, con partes de programación funcional y programación asíncrona. Pero el sistema de la orientación a objetos aquí es distinto. Aunque Alan Kay, creador de Smalltalk y casi casi de la orientación a objetos, opine que el estilo de C++ y de Java son putas mierdas, lo cierto es que es lo más usado actualmente. Herencia, clases, interfaces, etc

Personalmente prefiero C#, ya que como lenguaje es más potente que Java, pero ambos al final tienen bastantes características comunes entre sí.

Para C# el compilador más usado es Roslyn, actualmente disponible en .NET Framework, .NET Core y Mono.

Otros lenguajes parecidos son Java o si preferimos una sintaxis tipo Pascal: Delphi/Object-Pascal tiene conceptos muy similares.

Conclusión

Con esto ya tendríamos 5 lenguajes suficientemente diferentes. Ahora bien, nos hemos dejado lenguajes muy interesantes, tanto desde el punto de vista práctico como teórico. No puedo dejar de mencionar a Smalltalk por su implementación de los objetos, a C por su ligera capa de abstracción sobre ensamblador, al propio Ensamblador porque es lo que ejecuta la CPU realmente, a Python por su diseño así como su gran uso en ciencia de datos y scripts o a Rust, un lenguaje imperativo con sistema de traits, semántica de movimiento, pattern matching.

Merece también la pena mirar FORTH, SQL (quizá el lenguaje declarativo más usado del mundo) y lenguajes que se adapten bien a la programación reactiva (hay sobre todo librerías, aunque algún lenguaje como Simulink lo implementa). Mathemathica implementa también el paradigma de programación simbólica, muy poco explotado en otros lenguajes.

Estos han sido mis 5 lenguajes, ¿cuáles serían los tuyos?

Yew, crea webapps al estilo Angular/React en Rust

Hoy os traigo una librería muy potente y muy útil, ahora que Rust compila a WebAssembly de forma nativa. Se trata de Yew, una librería para diseñar frontends, single-page-applications y en general, una librería que es una alternativa a Angular, React, Vue y Elm.

En particular, Yew se basa en The Elm Architecture, por lo que los usuarios de Elm serán los que encuentren más familiar a Yew.

Yew significa tejo. He aquí uno de los tejos más famosos de Valladolid, en la plaza del Viejo Coso

Instalar cargo-web y el soporte a WebAssembly

Antes de empezar a programar necesitaremos instalar un plugin para Cargo, llamado cargo-web, que nos ayudará en el desarrollo web con Rust. Por otro lado, hace falta instalar el soporte de Rust a WebAssembly. Existen tres opciones actualmente: asmjs-unknown-emscripten, wasm32-unknown-emscripten y wasm32-unknown-unknown. Para los primeras opciones hace falta tener instalado Emscripten. Para la última, no hace falta nada, por lo que es mi opción recomendada. Por otro lado, wasm32 significa WebAssembly y asmjs es asm.js, que es simplemente JavaScript y será compatible con más navegadores.

The Elm Architecture

Los principios de The Elm Architecture se basan en: modelo, mensajes, actualización y vista.

Para este tutorial vamos a hacer la típica aplicación de una lista donde guardamos notas. Nuestro modelo se va a componer de una lista de tareas, para simplificar, pongamos que una tarea es simplemente un String y un ID. Entonces también nos hará falta almacenar un contador para ir poniendo IDs. También, al tener un campo de texto, nos hará falta una variable para ir almacenando temporalmente lo que escribe el usuario.

Lo siguiente es diseñar los mensajes. Los mensajes interactúan con el modelo y desencadenan una actualización de la vista. En esta aplicación solo nos hacen falta dos mensajes: añadir mensaje y borrar mensaje. Pero como tenemos un campo de texto, tenemos que introducir un mensaje Change y siempre viene bien un mensaje que no haga nada.

Una vez hecho esto pasamos a crear la función update, en la que hacemos distintas cosas según el mensaje que recibamos.

Así pues, si se lanza un mensaje Msg::Add lo que hacemos es copiar el valor de la variable temporal input, crear una nueva tarea con su ID y añadirla a la lista de tareas. Ya está. Yew mágicamente actualizará la página para reflejar que la lista de tareas ha sido modificada. Lo mismo pasa con Remove.

Ahora vamos a las vistas. Una vista es una función que devuelve Html<Msg> y se pueden componer varias funciones así. En nuestro caso, tenemos una vista principal donde se ve un campo de texto y un sitio donde se ejecuta un bucle for con las tareas del modelo. Y a cada tarea se le aplica la vista view_task.

La macro html! nos permite escribir HTML directamente en Rust, con algunas diferencias (¡prestad atención a las comas!). También nos permite introducir código Rust (entre llaves) y closures (observad onclick, oninput y onkeypress).

Finalmente en el método main, inicializamos el modelo y llamamos a program, que empieza a ejecutar Yew.

El código final queda así.

Ejecutando la aplicación web

Usando cargo web, es muy sencillo generar la aplicación web. Simplemente ejecuta:

El resultado, se montará en un mini servidor web. Si accedes a la URL que indica Cargo con tu navegador web, verás algo similar a esto:

Añade items y bórralos. Observa como la aplicación funciona perfectamente.

Distribuyendo la aplicación

Realmente cargo web ha hecho muchas cosas por nosotros. Si nosotros queremos usar Yew en la vida real, no usaremos cargo web. Para ello, compilamos la aplicación web:

Y accedemos a la carpeta target/wasm32-unknown-unknown/release. Allí encontraremos dos archivos que vamos a necesitar. Uno acabado en .js y otro acabado en .wasm. Ambos ficheros deberemos copiarlos donde queramos usarlos. Por último, será necesario un fichero HTML. En el fichero HTML solo hace falta cargar el fichero JS. Yew hará el resto.

Si quieres saber más, puedes descargarte el código de ejemplo que se encuentra en GitHub.

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)).