Rust por encima, comparando con erlang
Durante el 2020 he dedicado tiempo a aprender erlang
. Ya he dicho
muchas veces que me gustan los lenguajes veteranos y supongo que
alguien que lea sólo el título estará pensando que voy a traicionar
esos gustos. Sin embargo, estos días encontré en algún sitio un enlace
a un artículo que comparaba las características de erlang
con las de
Rust
, lo que me lleva a evaluarlo como próxima adquisición. En este
artículo hablaré de lo que he ido cotilleando sobre este lenguaje y
por qué me ha interesado. También he leído que es un poco puñetero y
que lanza errores constantemente que no te permiten compilar y tienes
que retocar el código, pero entiendo que eso forma parte de la
seguridad que maneja a la hora de crear los ejecutables, por tanto
¿eso es bueno o es malo?
Después de trabajar un poco y hacer algún que otro proyecto con
erlang
, le he encontrado algunas características que me han
encantado y que me gustaría tener en cualquier otro lenguaje. El caso
es que mirando cosas sobre erlang
llegué a un artículo que lo
comparaba con Rust. La tentación de probar el lenguaje fue grande,
porque algunas características de las que me encantan de erlang
están presentes en dicho lenguaje. Además cuando me puse a investigar
un poco más, todos alaban la seguridad y la velocidad de los
ejecutables que genera.
Características
Quizá sea más fácil si enumero las características de erlang
que me
gustan y vamos a ver cuáles proporciona Rust
. Mirando en la
documentación encontré una lista de lenguajes que han influido a éste.
El caso es que aparecen en ella algunos lenguajes que me resultan
gratos:
erlang
: de éste toman el paso de mensajes y ha influido en otras características.scheme
: de él toma los macros.ruby
: de aquí viene la sintaxis de closures.
Hay varios lenguajes más que tienen características interesantes:
Haskell
, OCaml
, C++
, etc. Pero vamos a ver las cosas que me
gustaría encontrar y que erlang
me proporciona.
Inmutabilidad
En erlang
la asignación de las variables no puede cambiar. De
primeras puede parecer complicado de entender que las variables no
varíen, pero a la larga es una bendición el que no lo hagan. Cada
expresión es siempre idéntica a cuando ha sido definida y se eliminan
todos los errores que suceden en otros lenguajes cuando una variable
cambia a un valor no esperado.
El caso es que Rust
tiene variables que pueden mutar, pero por
defecto son inmutables. También supongo que muchos errores de
compilación de los que se quejan algunos usuarios pueden venir de este
lado: intentar asignar un nuevo valor a una variable inmutable. Por lo
tanto, si al final se decide que sea mutable tendrás que pensar por
qué, qué ventajas tiene poder modificarla y si el proceso de hacerlo
es suficientemente seguro.
Concordancia de patrones
Esta es una de las características que más me han gustado de erlang
.
Comprobar no sólo los valores, sino la estructura o el tipo de las
variables y reaccionar a esos patrones con el código correspondiente
es una forma muy potente, sencilla y rápida de escribir código que se
comporta de modo polimórfico. Porque además, esa comprobación se
puede llevar a cabo en cualquier parte del código: en las llamadas,
dentro de las funciones, es igual. Permite una flexibilidad enorme a
la hora de escribir código.
Por lo que he visto Rust
no llega al mismo nivel y también tiene la
posibilidad de utilizar la concordancia de patrones en estructuras de
código parecidas a las estructuras case
de erlang
. Cuando avance
un poco más en el uso de este lenguaje veré si es lo que espero. De
todas formas, cada lenguaje tiene sus caminos.
Tuples y listas
La manera en la que erlang
utiliza las estructuras de datos, no sólo
los tuples, pero pincipalmente éstos, es una forma muy inteligente
de organizar la información con la que trabaja el programa. Una vez
que defines un tuple o una lista, se mantienen inmutables. Pero
puedes organizar los datos de cualquier manera que te resulte cómodo y
luego descomponerlo con patrones de concordancia de manera rápida,
con una asignación.
Por lo que he visto en Rust
también hay tuples y equivalentes a
las listas tiene vectores y arrays. Uno de esos tipos es inmutable
(arrays), más recomendable cuando se conoce el tamaño en tiempo de
compilación. Mientras que los vectores son conjuntos de datos que
pueden ajustarse en tiempo de ejecución. Veremos cómo se comportan
estos tipos de datos.
Registros y ETS1
Los registros son estructuras que pueden almacenar cualquier tipo de
dato válido de erlang
. Son estructuras fijas que se definen junto al
programa y no pueden cambiarse durante la ejecución. No se pueden
añadir o quitar campos en medio de la ejecución. Además esos
registros se pueden organizar en tablas ETS, ordenados por un campo
que funciona como índice de la tabla. Las tablas se pueden compartir
entre varios procesos, por lo que funciona como una forma de compartir
datos entre los distintos componentes del sistema. Si no quieres que
la tabla sea compartida se puede definir un proceso propietario que
sea el único encargado de acceder a ella, como lectura/escritura o
como escritura, de esta forma se puede garantizar la integridad de
los datos. Todo esto, sin mencionar Mnesia
que es una base de datos
que puedo organizar esas tablas ETS y almacenar datos relacionados
en ellas. No es SQL
pero se pueden organizar consultas
equivalentes.
Parece ser que Rust
proporciona structs. Sin embargo, no parece
que haya algo similar a ETS. Aunque supongo que un vector de
structs puede servirnos también, no es lo mismo que tener tablas con
sus índices para encontrar los datos. Espero que la velocidad del
lenguaje compense la falta de estructura en este sentido y sea fácil
conseguir algo parecido.
Iteración
Como muchos otros lenguajes funcionales, erlang
proporciona el
mecanismo de iteración como su principal modo para realizar bucles.
Además, en la librería lists
proporciona algunas funciones
iterativas como map
, foreach
, filter
, etc. Además, la potencia
de la iteración sobre la estructura cabeza-cola, [H|T]
, como en
otros lenguajes funcionales es algo que se emplea con profusión. Eso
sin contar los maravillosos bloques de código, que incluso Python
copió para realizar operaciones rápidas, tipo:
%% Devuelve una lista con el doble de los términos impares [X * 2 || X <- List, (X rem 2) /= 0].
Parece ser que Rust
cuenta con las instrucciones de bucle habituales
de otros lenguajes como while
, for
, etc. Parece que está menos
enfocado a la iteración, aunque parece que también se puede utilizar,
no es su forma óptima. Entiendo, que en este caso será mejor utilizar
esas estructuras de bucle en lugar la iteración.
Comunicación entre procesos
Al principio, la forma de comunicarse que tienen los procesos de
erlang
me pareció liosa. En cuanto comprendes que funciona de manera
asíncrona, como enviar un mensaje depositándolo en un buzón y esperar
a que te contesten ─o no─, te das cuenta que es lo más simple que hay.
Al final, montar un componente es tan sencillo como levantar un
proceso, abrir un buzón para él e intercambiar mensajes con los
demás procesos.
La concurrencia es una de esas cosas que le dan caché a erlang
y
es la principal característica que mencionan todos para decantarse en
el uso de este lenguaje. Los procesos concurrentes de otros lenguajes,
compartiendo memoria, estableciendo semáforos o banderas, bloqueos,
etc. es algo mucho más lioso que el mecanismo de erlang
.
Por lo que he leído hasta ahora sobre Rust
, también está enfocado a
la concurrencia. Aún no sé qué mecanismos involucra.
Documentación
La documentación con la que viene erlang
es completa y suficiente
para consultas rápidas de las distintas librerías con las que puedes
trabajar. Además son accesibles desde el propio Emacs para realizar
esas consultas.
Por lo que he visto, Rust
es bastante agradecido en este aspecto
también y viene con una documentación bastante completa. Además en su
página web puedes encontrar enlaces donde poder aprender y consultar
los distintos aspectos del lenguaje. Además, después de mirar un poco
por encima, la ayuda viene en ficheros html
y para leerla
cómodamente se lanza el navegador.
Rust
El caso es que me he puesto a investigar algo más sobre Rust
y a
parte de todo lo que se puede encontrar en la wikipedia, donde te
explica que es un lenguaje desarrollado por la Fundación Mozilla para
el desarrollo de bajo nivel, o de sistemas, superando las
dificultades que presenta C, he visto en su página algo más de
información.
La curiosidad, antes de meterme en faena, sin tener muy claro por qué merece la pena aprenderlo, me llevó a investigar si a priori el esfuerzo compensaría. Por ejemplo, siendo un lenguaje compilado me pregunté cómo sería su desempeño. Sin tener conocimientos aún suficientes para hacer comparaciones entre lenguajes busqué datos para compararlo en Benchmark Game. Concretamente:
Compararlo con otros lenguajes como Java
, Python
o erlang
me
parece injusto, porque no son lenguajes diseñados para lo mismo.
En cuanto a los resultados, como se puede apreciar Rust
está
codeándose con C y C++ en cuanto a resultados de velocidad. Por
ese lado parece un lenguaje interesante si efectivamente alcanza esa
velocidad siendo más seguro que C.
Quise probar algunos pequeños programillas, sólo por curiosidad e
instalé primero, los paquetes de Rust
que hay en OpenSuse, aunque
en la documentación prefieren descargar (luego veremos ese método) los
programas de su sitio web. De momento me centré en hacer unas pocas
pruebas de compilación tomando tiempos con lo que tenía más a mano,
cortando y pegando código encontrado en algunas páginas.
Después de esas pruebas, que ya habían aumentado mi curiosidad,
también investigué la popularidad de Rust
. Lo he encontrado en el
puesto 26 en enero de 2021. Sí, sigue sin estar en la cúspide de la
popularidad, pero tampoco está de los últimos. No es que me importe
mucho la popularidad de un lenguaje cuando me planteo aprender uno. De
hecho, como mis motivaciones no son curriculares o económicas, sino
que sólo me muevo por la pura curiosidad, puedo elegir aquellos
lenguajes que me resultan más llamativos o curiosos y así termino
dedicándole horas a lenguajes como smalltalk
, lisp
, scheme
,
erlang
... Todos lenguajes muy interesantes, pero poco populares o
usados en proyectos.
Configurar el entorno de trabajo
El entorno de trabajo, como se puede imaginar cualquiera que haya leído un poco este blog, es Emacs. Pero vamos por partes.
Instalación de Rust
Como he dicho antes, en el manual de Rust
realiza la instalación
desde su sitio con un simple comando:
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
En realidad, yo lo he hecho en dos pasos, ─ya me perdonaréis si peco de prudente─, pero no me fío de ejecutar un script bajado de la Internet así como así. Primero lo descargué y después de estudiar el script lo ejecuté:
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf > sh.rustup.rs
$ bash sh.rustup.rs
El estudio del script, tampoco ha sido exhaustivo, al final lo que
hace es descargar herramientas del sitio de Rust
. Por lo menos
comprobé que los enlaces son congruentes. Lo demás es confiar que esos
ejecutables han sido compilados y se mantienen con las medidas de
seguridad esperada. Al menos recomiendan forzar el uso de cifrado
TLS en la conexión de descarga que la hace algo más segura. La
confianza en este caso sería la misma que puedes depositar en los
ejecutables que te bajas de cualquier repositorio de software libre.
Cuando ejecutas el script, aparece un menú preguntando el tipo de
instalación con tres alternativas: instalación por defecto, avanzada o
cancelar. Elegí la opción 1
, la instalación por defecto. Después de
unos momentos en que descargó los paquetes informó que la instalación
ha tenido éxito. En la pantalla aparece un mensaje completo con
información sobre el lugar donde se instala2. Hay que añadir ese
directorio al $PATH
. Una vez hecho basta comprobar si la instalación
es correcta y funciona:
$ rustc --version rustc 1.49.0 (e1884a8e3 2020-12-29) $ cargo --version cargo 1.49.0 (d00d64df9 2020-12-05)
Una vez instalado se puede actualizar la instalación con:
$ rustup update
o desinstalarlo con:
$ rustup uninstall
o llamar a la documentación con:
$ rustup doc
Como se puede apreciar un entorno completo.
Preparar el editor
Aquí llega el momento de instalar paquetes en Emacs y convertirlo en
un auténtico IDE para Rust
. No lo he probado mucho aún, pero lo
que he visto me ha gustado. Los paquetes que he instalado son los
siguientes:
rustic
: proporciona el modo mayor de Emacs. Funciona sinlsp-mode
pero si lo tienes instalado mejor.flymake-rust
: compilación sobre la marcha buscando errores y avisos que se pueden corregir antes de pedir la compilación.ob-rust
: para poder gestionar bloques de códigoRust
enorg-mode
.
La configuración que he puesto en mi init.el
es muy sencilla. He
añadido el paquete rustic
al inicio:
(require 'rustic)
También, si usas lsp-mode
hay que activar el hook para el
lenguaje:
(add-hook 'rustic-mode-hook #'lsp)
Probando todo
Bueno pues vamos a por el Hola mundo correspondiente.
~ $ cd proyectos ~/proyectos $ mkdir hola-mundo ~/proyectos $ cd hola-mundo/ ~/proyectos/hola-mundo $ emacs main.rs
en ese fichero el contenido será:
fn main() { println!("Hola, mundo!"); }
y compilamos
~/proyectos/hola-mundo $ rustc main.rs ~/proyectos/hola-mundo $ ./main Hola, mundo!
y todo funciona perfectamente. Ahora a probar... ya iré contando cómo van las cosas.
Comentarios