Introducción: Por qué Rust es el lenguaje ideal para Serverless
La arquitectura serverless se ha convertido en el estándar para las aplicaciones modernas en la nube. Sin embargo, los lenguajes clásicos (Python, Node.js) a menudo sufren de un arranque en frío lento y un alto consumo de memoria. Rust resuelve estos problemas: sus binarios compilados se inician en milisegundos y el consumo de memoria es mínimo. En este artículo, analizaremos cómo crear, desplegar y optimizar funciones de AWS Lambda en Rust, desde la configuración del entorno hasta el despliegue en producción.
1. Configuración del entorno y herramientas
Instalación de Rust y Cargo Lambda
Para trabajar con AWS Lambda en Rust necesitarás:
- Rust (instalación a través de
rustup) - Cargo Lambda — la herramienta oficial para compilación y despliegue
- AWS CLI con credenciales configuradas
# Instalación de Rustcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Instalación de Cargo Lambdacargo install cargo-lambda
# Verificacióncargo lambda --versionCreación de un nuevo proyecto
Cargo Lambda genera automáticamente una plantilla con soporte para Lambda Runtime API:
cargo lambda new my-rust-lambdacd my-rust-lambdaLa estructura del proyecto incluye Cargo.toml con las dependencias lambda_runtime y serde, así como un archivo src/main.rs con una función controladora básica.
2. Escritura de la primera función Serverless en Rust
Estructura básica del controlador
Veamos una función Lambda simple que recibe un JSON con un nombre y devuelve un saludo:
use lambda_runtime::{service_fn, LambdaEvent, Error};use serde::{Deserialize, Serialize};
#[derive(Deserialize)]struct Request { name: String,}
#[derive(Serialize)]struct Response { message: String, request_id: String,}
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> { let req = event.payload; let resp = Response { message: format!("¡Hola, {}! ¡Estás en Rust!", req.name), request_id: event.context.request_id, }; Ok(resp)}
#[tokio::main]async fn main() -> Result<(), Error> { lambda_runtime::run(service_fn(handler)).await}Puntos clave:
service_fnenvuelve la función asíncrona en un controladorLambdaEventcontiene el payload y el contexto (request_id, deadline, etc.)- Serialización/deserialización a través de
serde
Pruebas locales
Antes del despliegue, probemos la función localmente:
cargo lambda watch# En otra terminal:cargo lambda invoke --data-ascii '{"name": "Mundo"}'Deberías obtener la respuesta: {"message":"¡Hola, Mundo! ¡Estás en Rust!","request_id":"..."}
3. Trabajo con servicios de AWS (S3, DynamoDB)
Integración con S3 a través de AWS SDK
Agreguemos a Cargo.toml:
[dependencies]aws-sdk-s3 = "1.0"aws-config = "1.0"Ejemplo de una función que lee un objeto de S3:
use aws_sdk_s3::Client;
async fn handler(event: LambdaEvent<Request>) -> Result<Response, Error> { let config = aws_config::load_from_env().await; let s3_client = Client::new(&config);
let bucket = "my-bucket"; let key = &event.payload.name;
let object = s3_client .get_object() .bucket(bucket) .key(key) .send() .await?;
let body = object.body.collect().await?; let content = String::from_utf8(body.to_vec())?;
Ok(Response { message: format!("Contenido del archivo: {}", content), request_id: event.context.request_id, })}En