Como mencionamos en la entrega anterior, Ballerina es un lenguaje de programación en crecimiento. No posee una comunidad tan grande como otros lenguajes (Java, C#, C/C++, Python, etc.), pero así empiezan todos. Ballerina junto otros lenguajes como Go y Rust han cobrado cierto interés en el mundo de la programación, servicios Cloud, servicios REST y hasta la I.A.
En esta ocasión haremos una comparativa con Rust, un lenguaje de programación que tiene inspiración en lenguajes como C y C++, tomando lo mejor de ellos y descartando lo que no es necesario y Ballerina, un lenguaje que ha cobrado importancia en el desarrollo en la nube. Ambos lenguajes han aprendido de los mejores e incluso han ido superándolos incluso en velocidad y rendimiento.
Ballerina y Rust son lenguajes muy distintos: Ballerina está orientado a integración y microservicios, mientras que Rust se centra en sistemas de alto rendimiento y seguridad de memoria.
Veamos una tabla comparativa:
| Aspecto | Ballerina | Rust |
|---|---|---|
| Paradigmas | Multiparadigma con foco en integración, concurrencia y servicios distribuidos; código con visualización de flujos y llamadas. | Multiparadigma (imperativo y funcional) orientado a sistemas, control de bajo nivel y seguridad de memoria. |
| Casos de uso | Integración de APIs, microservicios, orquestación de endpoints, aplicaciones cloud-native y seguridad en comunicaciones. | Sistemas operativos, motores de juegos, programación embebida, CLI y servicios de alto rendimiento. |
| Compilación y ejecución | Compila con bal build; ejecuta con bal run archivo.bal. Soporte integrado para Docker/Kubernetes. |
Compila con rustc archivo.rs; ejecuta con ./archivo. En proyectos se usa Cargo (cargo build, cargo run). |
| Tipos de variables | Tipado estático: int, float, boolean, string; estructurados: record, map, array, table. |
Tipado estático con inferencia: primitivos (i32, f64, bool, char) y avanzados (struct, enum, tuple). |
| Estructuras de control | if, while, foreach, match; concurrencia con workers y fork/join. |
if, loop, while, for, match con patrones; concurrencia segura con async/await y ownership. |
| Estructuras de datos | record (tipo estructurado), map, array, table. |
struct, enum, tuple, array, Vec<T>, HashMap<K,V>. |
| Concurrencia | Modelo de trabajadores, transacciones y flujos de integración; primitivas para servicios distribuidos. | Seguridad de datos en concurrencia mediante ownership, borrowing y tipos como Arc/Mutex. |
| Ecosistema | Enfocado en integración (HTTP, gRPC, GraphQL, Kafka) con librerías estándar orientadas a servicios. | Amplio ecosistema de crates para sistemas, redes, WebAssembly, embebidos y desarrollo multiplataforma. |
| Curva de aprendizaje | Suave para integraciones y microservicios; diseño opinado para flujos de red. | Pronunciada por el ownership y el borrow checker; recompensa con rendimiento y seguridad. |
Puntos clave entre Ballerina y Rust
Ambos lenguajes tienen similitudes y diferencias, aquí los puntos claves:
- Enfoque distinto: Ballerina es un lenguaje de integración; Rust es un lenguaje de sistemas.
- Compilación: Rust ofrece control de bajo nivel y optimización; Ballerina simplifica despliegues en entornos distribuidos.
- Datos y control: Rust tiene estructuras más ricas y flexibles; Ballerina prioriza simplicidad y seguridad en datos de red.
- Concurrencia: Ambos soportan concurrencia, pero Rust lo hace con un modelo de ownership y Ballerina con workers y diagramas visuales.
Ejemplo 1. Crear un sencillo "Hola, mundo" en Ballerina y Rust. Esto nos servirá para ver cómo se compila y ejecuta un programa en cada lenguaje.
Empecemos con Ballerina. Compilaremos y ejecutaremos un archivo llamado holamundo.bal:
import ballerina/io; public function main(string... args) { io:println("\t Hola, mundo en Ballerina"); }
Compilamos y ejecutamos:
$ bal run holamundo.bal
El código se compilará y se ejecutará en la terminal, la salida será:
Compiling source holamundo.bal Running executable Hola, mundo en Ballerina
Ahora con Rust, compilaremos y ejecutaremos el archivo holamundo.rs:
fn main() { println!("\t Hola mundo en Rust"); }
Compilamos:
$ rustc holamundo.rs
Se creará un ejecutable:
$ holamundo.exe
Salida:
Hola mundo en Rust
¿Qué podemos notar?
- Ambos lenguajes son compilados.
- Ballerina es más similar Java, es dependiente de la JVM y genera bytecodes.
- Rust es más similar a C/C++, genera ejecutables nativos (*exe).
- La extensión de un programa Ballerina es *bal.
- La extensión de un programa Rust es *rs.
Tipos de datos en Ballerina y Rust
Como mencioanmos, ambos lenguajes son compilados. También comparten la característica de ser de tipado estático. Eso quiere decir que debes especificar el tipo de datos a usar. No como lenguajes como Python o Ruby donde "una variable es una variable".
| Categoría | Ballerina | Rust |
|---|---|---|
| Primitivos numéricos | int, float, decimal |
i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 |
| Booleanos | boolean |
bool |
| Texto y caracteres | string |
char, String (tipo de colección) |
| Compuestos | record, map, array, table |
struct, enum, tuple, array, Vec<T>, HashMap<K,V> |
| Especiales | nil (valor nulo), error, any |
Option<T>, Result<T,E>, unit (()) |
| Concurrencia | Workers y canales integrados para paso de mensajes | Tipos seguros para concurrencia: Arc, Mutex, RwLock |
Ejemplo 2. Crear un programa en ambos lenguajes con el objetivo de ver los tipos de datos en cada lenguaje.
En Ballerina:
tipos.bal
import ballerina/io; type Persona record {| string nombre; int edad; |}; enum Estado { ACTIVO, INACTIVO } public function main() { int edad = 30; float altura = 1.75; boolean activo = true; string inicial = "C"; // no existe 'char' string nombre = "Thomas Muller."; int[] numeros = [1, 2, 3, 4]; [int, float, string] tupla = [25, 1.80, "Ana"]; Persona p = { nombre: "Luis", edad: 40 }; Estado estado = ACTIVO; map<string> capitales = { "MX": "Ciudad de México", "US": "Washington" }; int|error resultado = 10; // Result float? opcion = 3.14; // Option int contador = 0; fork { worker w1 { int local = contador; local += 1; io:println("Worker 1 contador: " , local.toString()); } worker w2 { int local = contador; local += 1; io:println("Worker 2 contador: " , local.toString()); } } io:println("Nombre: " + nombre + ", Edad: " + edad.toString()); string capitalMX = capitales["MX"] ?: "Desconocido"; io:println("Capital de MX: " + capitalMX); io:println("Persona: " + p.toString()); io:println("Contador final (simulado): " + contador.toString()); }
Compilamos y ejecutamos:
$ bal run tipos.bal
Salida:
Nombre: Thomas Muller., Edad: 30 Capital de MX: Ciudad de México Persona: {"nombre":"Luis","edad":40} Contador final (simulado): 0 Worker 2 contador: 1 Worker 1 contador: 1
En Rust:
tipos.rs
fn main() { let edad: i32 = 30; let altura: f64 = 1.75; let activo: bool = true; let inicial: char = 'C'; let nombre: String = String::from("Thomas Muller."); let numeros: [i32; 4] = [1, 2, 3, 4]; let tupla: (i32, f64, &str) = (25, 1.80, "Ana"); struct Persona { nombre: String, edad: i32, } let p = Persona { nombre: String::from("Luis"), edad: 40 }; enum Estado { Activo, Inactivo, } let estado = Estado::Activo; use std::collections::HashMap; let mut capitales = HashMap::new(); capitales.insert("MX", "Ciudad de México"); capitales.insert("US", "Washington"); let resultado: Result<i32, &str> = Ok(10); let opcion: Option<f64> = Some(3.14); use std::sync::{Arc, Mutex}; use std::thread; let contador = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..2 { let contador = Arc::clone(&contador); let handle = thread::spawn(move || { let mut num = contador.lock().unwrap(); *num += 1; }); handles.push(handle); } for h in handles { h.join().unwrap(); } println!("Nombre: {}, Edad: {}", nombre, edad); println!("Capital de MX: {}", capitales["MX"]); println!("Persona: {} ({})", p.nombre, p.edad); println!("Contador final: {}", *contador.lock().unwrap()); }
Compilamos:
$ rustc tipos.rs
Se creará un ejecutable:
$ tipos.exe
Salida:
Nombre: Thomas Muller., Edad: 30 Capital de MX: Ciudad de México Persona: Luis (40) Contador final: 2
Podríamos continuar con las comparaciones con estructuras de datos, acceso a archivos, acceso a base de datos y hasta servicios REST. Sin embargo, lo haremos en otra ocasión.
Conclusiones
Es importante hacer notar que:
- Rust: curva de aprendizaje más pronunciada por su sistema de ownership y borrow checker.
- Ballerina: menos comunidad y ecosistema que Rust; puede ser limitante fuera del ámbito de integración.
Ambos lenguajes tienen un propósito, resuelven problemas distintos. No son competidores.
Enlaces:
https://rust-lang.org/https://ballerina.io/
https://www.infoq.com/articles/ballerina-integration-tutorial-part-2/
https://www.infoq.com/articles/ballerina-microservices-language-part-1/


No hay comentarios:
Publicar un comentario