Introduction: Why Rust is the Ideal Language for Serverless
Serverless architecture has become the standard for modern cloud applications. However, classic languages (Python, Node.js) often suffer from slow cold starts and high memory consumption. Rust solves these problems: its compiled binaries start in milliseconds, and memory consumption is minimal. In this article, we will cover how to create, deploy, and optimize AWS Lambda functions in Rust — from environment setup to production deployment.
1. Environment and Tools Setup
Installing Rust and Cargo Lambda
To work with AWS Lambda in Rust, you will need:
- Rust (install via
rustup) - Cargo Lambda — the official tool for building and deploying
- AWS CLI with configured credentials
# Installing Rustcurl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Installing Cargo Lambdacargo install cargo-lambda
# Verificationcargo lambda --versionCreating a New Project
Cargo Lambda automatically generates a template with Lambda Runtime API support:
cargo lambda new my-rust-lambdacd my-rust-lambdaThe project structure includes Cargo.toml with lambda_runtime and serde dependencies, as well as a src/main.rs file with a basic handler function.
2. Writing Your First Serverless Function in Rust
Basic Handler Structure
Let's look at a simple Lambda function that accepts JSON with a name and returns a greeting:
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!("Hello, {}! You are on 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}Key points:
service_fnwraps an async function into a handlerLambdaEventcontains the payload and context (request_id, deadline, etc.)- Serialization/deserialization via
serde
Local Testing
Before deployment, let's test the function locally:
cargo lambda watch# In another terminal:cargo lambda invoke --data-ascii '{"name": "World"}'You should receive a response: {"message":"Hello, World! You are on Rust!","request_id":"..."}
3. Working with AWS Services (S3, DynamoDB)
Integration with S3 via AWS SDK
Add to Cargo.toml:
[dependencies]aws-sdk-s3 = "1.0"aws-config = "1.0"Example function that reads an object from 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!("File content: {}", content), request_id: event.context.request_id, })}In