Rust vs Python para backend: ¿qué elegir en la práctica?
La elección del lenguaje de programación para el backend es una decisión fundamental que afecta la velocidad de desarrollo, el rendimiento y el costo de mantenimiento del proyecto. Python ha sido durante mucho tiempo el rey del backend gracias a su simplicidad y enorme cantidad de bibliotecas. Sin embargo, Rust, un lenguaje de programación de sistemas, irrumpió rápidamente en el mundo del desarrollo web, ofreciendo seguridad de memoria y rendimiento a nivel de C++.
En este artículo no discutiremos teorías abstractas. Analizaremos escenarios prácticos concretos: escribir un servidor HTTP, trabajar con una base de datos y procesar solicitudes concurrentes. Verá cómo se ve el mismo código en Rust y Python, y podrá tomar una decisión informada para su próximo proyecto.
Rendimiento: cuando la velocidad importa
Python es un lenguaje interpretado con tipado dinámico. Esto brinda flexibilidad, pero el precio es la velocidad. Rust se compila a código máquina y no tiene recolector de basura, lo que lo convierte en uno de los lenguajes más rápidos.
Consideremos un ejemplo simple: un servidor HTTP que procesa una solicitud GET y devuelve JSON.
Python (FastAPI)
from fastapi import FastAPIimport uvicorn
app = FastAPI()
@app.get("/")async def read_root(): return {"message": "Hello, World!"}
if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)Rust (Axum)
use axum::{Router, routing::get, Json};use serde_json::{Value, json};use std::net::SocketAddr;
async fn root() -> Json { Json(json!({"message": "Hello, World!"}))}
#[tokio::main]async fn main() { let app = Router::new().route("/", get(root)); let addr = SocketAddr::from(([0, 0, 0, 0], 8000)); println!("Server running on {}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap();} Resultados de las pruebas (wrk, 100 conexiones, 10 segundos):
- Python (FastAPI): ~12,000 RPS
- Rust (Axum): ~280,000 RPS
Rust muestra un rendimiento 20+ veces superior. Si su proyecto es una API de alta carga, un microservicio o un sistema en tiempo real, Rust es la opción obvia. Python sigue siendo una buena opción para prototipos y herramientas internas con baja carga.
Seguridad y gestión de memoria
Python gestiona completamente la memoria a través del recolector de basura. Esto es conveniente, pero puede provocar pausas inesperadas (GC pauses) y fugas de memoria. Rust utiliza el sistema de propiedad (ownership) y préstamo (borrowing), lo que garantiza la seguridad de la memoria en tiempo de compilación.
Consideremos un ejemplo: una función que procesa una lista de usuarios y actualiza su saldo.
Python (con posible error)
users = [{"name": "Alice", "balance": 100}, {"name": "Bob", "balance": 200}]
def update_balance(user_list, amount): for user in user_list: user["balance"] += amount return user_list
# De repente alguien elimina un elementousers.pop(0)result = update_balance(users, 50) # Funciona, pero la lógica está rotaRust (código seguro)
struct User { name: String, balance: i32,}
fn update_balance(users: &mut Vec<User>, amount: i32) { for user in users.iter_mut() { user.balance += amount; }}
fn main() { let mut users = vec![ User { name: "Alice".to_string(), balance: 100 }, User { name: "Bob".to_string(), balance: 200 }, ]; // El compilador no permitirá cambiar el vector accidentalmente durante la iteración update_balance(&mut users, 50); // Intentar usar users después de moverlo provocará un error de compilación // let other = users; // ownership moved // println!("{}