- Why Rust
- Let's get Rusty
- The fun stuff - What's happening in the code
- Let's
blow things up, build and deploy
Whether Rust is the C++ killer we've all been waiting for remains to be seen. With companies like Tesla and Microsoft still heavily reliant on C++ it would appear there is a strong case for C++'s continued existence. Nonetheless, Rust's growing popularity among developers is definitely something to take notice of. [1] In 2019 AWS officially became a sponsor of the Rust project before partnering with Microsoft, Google, Mozilla and Huawei in 2020 to create the Rust Foundation, an organistion whose sole purpose is to support Rust and fuel its adoption as a general purpose language.
The rapid uptake of Rust by the three largest cloud tech companies has seen the Rust community grow considerably since Rust was first released in 2010. In saying so, it is becoming increasingly obvious that Rust skills will incrementally be in high demand well into the future.
As a programming language Rust has all the hallmarks of a mature programming language that has the capacity to handle projects of any level of complexity. My personal favourite Rust Feature is the provision of an inbuilt packgage manager called cargo
, (something I wish C++ came with). Rust also has other notable features, I have provided a list of some of them below:
- No run time garbage collection (grants control of memory management)
- Can target embedded systems
- Rich type system and ownership model that helps you weed out memory related bugs thereby ensuring memory and thread safety.
- Tiny footprint
- A package manager called Cargo (Mentioned it twice because it is that good)
-
Install Rust by following the instructions provided here
https://www.rust-lang.org/tools/install
-
Install zigbuild by running
cargo install cargo-zigbuild
in your command line. Zigbuild is a linker CLI tool that simplifies cross compilation of Rust programmes.
-
Create new Rust Project
cargo new <rust_project_name>
-
Open the project folder and install dependencies
cargo add lambda_runtime tokio serde_json serde
- Replace the contents of
src/main.rs
with the following code
use lambda_runtime::{Context, Error, service_fn, run, LambdaEvent};
use serde::{Deserialize, Serialize};
#[tokio::main]
async fn main () ->Result<(), Error>{
let handler = service_fn(handler);
run(handler).await?;
Ok(())
}
#[derive(Deserialize)]
struct Event {
first_name: String,
last_name: String
}
#[derive(Serialize)]
struct Output {
message: String,
request_id:String
}
async fn handler (event: LambdaEvent<Event>) -> Result<Output, Error> {
let message:String = format!("Hi {} {} , welcome to rust in the cloud!", event.payload.first_name, event.payload.last_name);
Ok(Output { message, request_id: event.context.request_id })
}
The block of code shown below imports dependencies into our code. At the very top we import lambda_runtime
the library that provides a Lambda runtime for our Rust code.
Next we import serde
a framework for serialising/deserialising Rust data structures in an efficient and generic manner.
use lambda_runtime::{Context, Error, service_fn, run, LambdaEvent};
use serde::{Deserialize, Serialize};
The next Block of code starts with the #[tokio::main]
macro. This macro is responsible for transforming the async fn main ()
,execution entry point, function into a synchronous function that initialises a runtime instance and then subsequently executes the async main function.
#[tokio::main]
async fn main () ->Result<(), Error>{
let handler = service_fn(handler); // This lets you build a Service from an async function that returns a Result.
run(handler).await?; //Starts the Lambda Rust runtime and begins polling for events on the Lambda Runtime APIs
Ok(())
}
#[derive(Deserialize)] // Allows the struct to be Deserialised
struct Event {
first_name: String,
last_name: String
}
#[derive(Serialize)] // Allows the struct to be Serialised
struct Output {
message: String,
request_id:String
}
The block of code provided below is the handler function that processes events. It is passed to the service_fn()
function so it can be converted to a service that can then be passed to the run()
function which has the responsibility of starting the Lambda Rust runtime
async fn handler (event: LambdaEvent<Event>) -> Result<Output, Error> {
let message:String = format!("Hi {} {} , welcome to rust in the cloud!", event.payload.first_name, event.payload.last_name);
Ok(Output { message, request_id: event.context.request_id })
}
-
Install the arm64 Rust target via rustup by running
rustup target add aarch64-unknown-linux-gnu
. Doing this will allows us to target thearm64
architecture when we build our binary executable. -
Build an
arm64
executable binary file using Zigbuild by running `cargo zigbuild --target aarch64-unknown-linux-gnu --release' from the root of the Rust project -
Copy the generated executable binary file to the root and rename it to bootstrap by running the following command from the root of the project
cp target/aarch64-unknown-linux-gnu/release/<project_name> bootstrap
-
Create a
template.yaml
file and and place it on the same level as the Rust project directory. -
Add the the following to the
template.yaml
file you created in step 1
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
HelloFriendFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: HelloFriendFunction
MemorySize: 128
Architectures: ["arm64"]
Handler: bootstrap
Runtime: provided.al2
Timeout: 5
CodeUri: <rust_project_name>/
Outputs:
FunctionName:
Value: !Ref HelloFriendFunction
Description: This is a simple Lambda Function Written in Rust
- From the same level as the
template.yaml
file, run the commandsam deploy --guided
- Once The deployment succeeds, take note of the created Lambda Function's name in the outputs
Invoke the newly deployed lambda using aws-cli in your terminal, the output will be placed in a file called output.json
aws lambda invoke \
--cli-binary-format raw-in-base64-out \
--region ap-southeast-2 \
--function-name SimpleProgram-HelloFriendFunction-6RzpX00nKe0F \
--payload '{"first_name": "Leon", "last_name":"Ticharwa"}' \
output.json
expected output
{
"message":"Hi Leon Ticharwa , welcome to rust in the cloud!",
"request_id":"47f4f611-f9fa-4bca-a88b-3c89dbf47465"
}
All done!
Congratulations on deploying your first Rust custom runtime and program to AWS Lambda. If you have any questions related to running Rust in the cloud please feel free to get in touch with the Mechanical Rock team.
[1] Sustainability with Rust, https://aws.amazon.com/blogs/opensource/sustainability-with-rust/#:~:text=We%20use%20Rust%20to%20deliver,%2C%20Amazon%20CloudFront%2C%20and%20more.