

# Rust で Lambda 関数を構築する
<a name="lambda-rust"></a>

Rust はネイティブコードにコンパイルするため、Lambda で Rust コードを実行するために専用のランタイムは必要ありません。代わりに、[Rust ランタイムクライアント](https://github.com/aws/aws-lambda-rust-runtime)を使用してプロジェクトをローカルに構築したら、[OS 専用のランタイム](runtimes-provided.md)を使用して Lambda にデプロイします。OS 専用のランタイムを使用するとき、Lambda は最新のパッチを使用してオペレーティングシステムを自動的に最新の状態に保ちます。

**Rust 用のツールおよびライブラリ**
+ [AWS SDK for Rust](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/getting-started.html): AWS SDK for Rust は、Amazon Web Services のインフラストラクチャサービスとやり取りするための Rust API を提供します。
+  [Lambda 用の Rust ランタイムクライアント](https://github.com/aws/aws-lambda-rust-runtime): Rust ランタイムクライアントにより、Rust で記述された Lambda 関数を簡単に実行できます。
+ [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html): Rust Lambda 関数の構築およびデプロイを簡素化する Cargo コマンドラインのツールによるサードパーティーのオープンソース拡張機能です。
+ 「[Lambda HTTP](https://github.com/aws/aws-lambda-rust-runtime/tree/main/lambda-http)」: このライブラリは、HTTP イベントと連動するラッパーを提供します。
+  「[Lambda 拡張機能](https://github.com/aws/aws-lambda-rust-runtime/tree/main/lambda-extension)」: このライブラリは、Rust で Lambda 拡張機能を作成するためのサポートを提供します。
+ 「[AWS Lambda イベント](https://crates.io/crates/aws_lambda_events)」: このライブラリは、一般的なイベントソース統合のタイプ定義を提供します。

**Rust 用のサンプル Lambda アプリケーション**
+ 「[基本的な Lambda 関数](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/basic-lambda)」: 基本的なイベントの処理方法を示す Rust 関数。
+ 「[エラー処理機能付き Lambda 関数](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/basic-error-handling)」: Lambda でカスタム Rust エラーの処理方法を示す Rust 関数。
+ 「[共有リソースを使用する Lambda 関数](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/basic-shared-resource)」: Lambda 関数を作成する前に共有リソースを初期化する Rust プロジェクト。
+ 「[Lambda HTTP イベント](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/http-basic-lambda)」: HTTP イベントを処理する Rust 関数。
+ 「[CORS ヘッダー付き Lambda HTTP イベント](https://github.com/aws/aws-lambda-rust-runtime/blob/main//examples/http-cors)」: Tower を使用して CORS ヘッダーを挿入する Rust 関数。
+ 「[Lambda REST API](https://github.com/aws/aws-lambda-rust-runtime/tree/main/examples/http-axum-diesel)」: Axum および Diesel を使用して PostgreSQL データベースに接続する REST API。
+ 「[サーバーレス Rust デモ](https://github.com/aws-samples/serverless-rust-demo/)」: Lambda のライブラリ、ログ記録、環境変数、AWS SDK の使い方を示す Rust プロジェクト。
+ 「[基本的な Lambda 拡張機能](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/extension-basic)」: 基本的な拡張イベントの処理方法を示す Rust 拡張機能。
+ 「[Lambda ログ Amazon Data Firehose 拡張機能](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/extension-logs-kinesis-firehose)」: Kinesis Data Firehose に Lambda ログの送信方法を示す Rust 拡張機能。

**Topics**
+ [

# Rust の Lambda 関数ハンドラーの定義
](rust-handler.md)
+ [

# Lambda コンテキストオブジェクトを使用して Rust 関数の情報を取得する
](rust-context.md)
+ [

# Rust での HTTP イベントの処理
](rust-http-events.md)
+ [

# .zip ファイルアーカイブを使用して Rust Lambda 関数をデプロイする
](rust-package.md)
+ [

# Rust Lambda 関数のレイヤーを操作する
](rust-layers.md)
+ [

# Rust Lambda 関数のログ記録とモニタリング
](rust-logging.md)

# Rust の Lambda 関数ハンドラーの定義
<a name="rust-handler"></a>

Lambda 関数*ハンドラー*は、イベントを処理する関数コード内のメソッドです。関数が呼び出されると、Lambda はハンドラーメソッドを実行します。関数は、ハンドラーが応答を返すか、終了するか、タイムアウトするまで実行されます。

このページでは、プロジェクトの初期化、命名規則、ベストプラクティスなど、Rust で Lambda 関数ハンドラーを操作する方法について説明します。このページには、注文に関する情報を取得し、テキストファイル受信を生成し、このファイルを Amazon Simple Storage Service (S3) バケットに配置する Rust Lambda 関数の例も含まれています。関数を書き込んだ後にデプロイする方法の詳細については、「[.zip ファイルアーカイブを使用して Rust Lambda 関数をデプロイする](rust-package.md)」を参照してください。

**Topics**
+ [

## Rust ハンドラープロジェクトのセットアップ
](#rust-handler-setup)
+ [

## Rust Lambda 関数のコードの例
](#rust-example-code)
+ [

## Rust ハンドラーの有効なクラス定義
](#rust-handler-signatures)
+ [

## ハンドラーの命名規則
](#rust-example-naming)
+ [

## 入力イベントオブジェクトの定義とアクセス
](#rust-handler-input)
+ [

## Lambda コンテキストオブジェクトへのアクセスと使用
](#rust-example-context)
+ [

## ハンドラーでの AWS SDK for Rust の使用
](#rust-example-sdk-usage)
+ [

## 環境変数にアクセスする
](#rust-example-envvars)
+ [

## 共有ステートを使用する
](#rust-shared-state)
+ [

## Rust Lambda 関数のコードのベストプラクティス
](#rust-best-practices)

## Rust ハンドラープロジェクトのセットアップ
<a name="rust-handler-setup"></a>

Rust で Lambda 関数を使用する場合、このプロセスにはコードの記述、コンパイル、コンパイルされたアーティファクトの Lambda へのデプロイが含まれます。Rust で Lambda ハンドラープロジェクトをセットアップする最も簡単な方法は、[AWS Lambda Runtime for Rust](https://github.com/aws/aws-lambda-rust-runtime) を使用することです。AWS Lambda Runtime for Rust という名称になっていますが、これは Lambda for Python、Java、Node.js と同様のマネージドランタイムではありません。AWS Lambda Runtime for Rust は、Rust での Lambda 関数の書き込みと AWS Lambda の実行環境とのインターフェイス接続をサポートするクレート (`lambda_runtime`) です。

次のコマンドを使用して、Rust Lambda 関数の構築とデプロイを簡素化する Cargo コマンドラインツールへのサードパーティーのオープンソース拡張である [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html) をインストールします。

```
cargo install cargo-lambda
```

`cargo-lambda` を正常にインストールしたら、次のコマンドを使用して新しい Rust Lambda 関数ハンドラープロジェクトを初期化します。

```
cargo lambda new example-rust
```

このコマンドを実行すると、コマンドラインインターフェイス (CLI) に Lambda 関数についての質問がいくつか表示されます。
+ **HTTP 関数** – [API Gateway](services-apigateway.md) または[関数 URL](urls-configuration.md) を介して関数を呼び出す場合は、**はい**と回答します。それ以外の場合は、**いいえ**と回答します。このページのコード例では、カスタム JSON イベントを使用して関数を呼び出すため、**いいえ**と回答します。
+ **イベントタイプ** – 事前定義されたイベントシェイプを使用して関数を呼び出す場合は、予想される正しいイベントタイプを選択します。それ以外の場合は、このオプションを空白のままにします。このページのコード例では、カスタム JSON イベントを使用して関数を呼び出すため、このオプションは空白のままにします。

コマンドが正常に実行されたら、プロジェクトのメインディレクトリを入力します。

```
cd example-rust
```

このコマンドは、`src` ディレクトリ内に `generic_handler.rs` と `main.rs` ファイルを生成します。`generic_handler.rs` を使用して、汎用イベントハンドラーをカスタマイズできます。`main.rs` ファイルにはメインアプリケーションロジックが含まれます。`Cargo.toml` ファイルには、パッケージに関するメタデータが含まれ、その外部依存関係が一覧表示されます。

## Rust Lambda 関数のコードの例
<a name="rust-example-code"></a>

以下の Rust Lambda 関数コードの例では、注文に関する情報を取得し、テキストファイル受信を生成し、このファイルを Amazon S3 バケットに配置します。

**Example `main.rs` Lambda 関数**  

```
use aws_sdk_s3::{Client, primitives::ByteStream};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::env;

#[derive(Deserialize, Serialize)]
struct Order {
    order_id: String,
    amount: f64,
    item: String,
}

async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> {
    let payload = event.payload;

    // Deserialize the incoming event into Order struct
    let order: Order = serde_json::from_value(payload)?;

    let bucket_name = env::var("RECEIPT_BUCKET")
        .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?;

    let receipt_content = format!(
        "OrderID: {}\nAmount: ${:.2}\nItem: {}",
        order.order_id, order.amount, order.item
    );
    let key = format!("receipts/{}.txt", order.order_id);

    let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
    let s3_client = Client::new(&config);

    upload_receipt_to_s3(&s3_client, &bucket_name, &key, &receipt_content).await?;

    Ok("Success".to_string())
}

async fn upload_receipt_to_s3(
    client: &Client,
    bucket_name: &str,
    key: &str,
    content: &str,
) -> Result<(), Error> {
    client
        .put_object()
        .bucket(bucket_name)
        .key(key)
        .body(ByteStream::from(content.as_bytes().to_vec()))  // Fixed conversion
        .content_type("text/plain")
        .send()
        .await?;

    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    run(service_fn(function_handler)).await
}
```

この `main.rs` ファイルには以下のコードのセクションが含まれます:
+ `use` ステートメント: これらを使用して、Lambda 関数に必要な Rust クレートとメソッドをインポートします。
+ `#[derive(Deserialize, Serialize)]`: この Rust 構造体で予想される入力イベントの形状を定義します。
+ `async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>`: これは**メインハンドラーメソッド**で、メインアプリケーションロジックが含まれています。
+ `async fn upload_receipt_to_s3 (...)`: これは、メイン `function_handler` メソッドによって参照されるヘルパーメソッドです。
+ `#[tokio::main]`: これは Rust プログラムのエントリポイントを示すマクロです。また、[Tokio ランタイム](https://docs.rs/tokio/latest/tokio/runtime/index.html)をセットアップして、`main()` メソッドが `async`/`await` を使用して非同期的に実行できるようにします。
+ `async fn main() -> Result<(), Error>`: `main()`関数はコードのエントリポイントです。その中で、メインハンドラーメソッドとして `function_handler` を指定します。

### サンプル Cargo.toml ファイル
<a name="rust-cargo-toml"></a>

次の `Cargo.toml` ファイルは、この関数に付随します。

```
[package]
name = "example-rust"
version = "0.1.0"
edition = "2024"

[dependencies]
aws-config = "1.5.18"
aws-sdk-s3 = "1.78.0"
lambda_runtime = "0.13.0"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
```

この関数が正しく機能するには、[実行ロール](lambda-intro-execution-role.md)で `s3:PutObject` アクションを許可する必要があります。また、必ず `RECEIPT_BUCKET` 環境変数を定義してください。呼び出しに成功したら、Amazon S3 バケットに受信ファイルが含まれているはずです。

## Rust ハンドラーの有効なクラス定義
<a name="rust-handler-signatures"></a>

ほとんどの場合、Rust で定義した Lambda ハンドラー署名の形式は次のようになります。

```
async fn function_handler(event: LambdaEvent<T>) -> Result<U, Error>
```

このハンドラーの場合:
+ このハンドラーの名前は `function_handler` です。
+ ハンドラーへの単数の入力はイベントで、タイプは `LambdaEvent<T>` です。
  + `LambdaEvent` は、`lambda_runtime` クレート由来のラッパーです。このラッパーを使用すると、呼び出しのリクエスト ID などの Lambda 固有のメタデータを含むコンテキストオブジェクトにアクセスできます。
  + `T` は逆シリアル化されたイベントタイプです。例えば、これは `serde_json::Value` にすることができます。これにより、ハンドラーは汎用 JSON 入力を取得できます。または、関数が特定の事前定義された入力タイプを想定している場合 `ApiGatewayProxyRequest` のようなタイプにすることもできます。
+ ハンドラーの戻り値タイプは `Result<U, Error>` です。
  + `U` は逆シリアル化された出力タイプです。Lambda が戻り値を JSON に変換できるように、`U` は `serde::Serialize` 特性を実装する必要があります。例えば、`U` は、`String`、`serde_json::Value` などの単純な型、または `Serialize` を実装している限りカスタム構造体にすることができます。コードが Ok(U) ステートメントに到達することは、実行が成功したことを示しています。また、関数は `U` タイプの値を返します。
  + コードでエラー (`Err(Error)` など) が発生すると、関数は Amazon CloudWatch のログにエラーを記録し、`Error` タイプのエラーレスポンスを返します。

この例では、ハンドラー署名は次のようになります。

```
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error>
```

その他の有効なハンドラー署名には、次の機能があります。
+ `LambdaEvent` ラッパーを省略する – `LambdaEvent` を省略すると、関数内の Lambda コンテキストオブジェクトにアクセスできなくなります。このタイプの署名の例を以下に示します。

  ```
  async fn handler(event: serde_json::Value) -> Result<String, Error>
  ```
+ 単位タイプを入力として使用する – Rust の場合、単位タイプを使用して空の入力を表すことができます。これは一般的に、定期的なスケジュールされた呼び出しを持つ関数に使用されます。このタイプの署名の例を以下に示します。

  ```
  async fn handler(_: ()) -> Result<Value, Error>
  ```

## ハンドラーの命名規則
<a name="rust-example-naming"></a>

Rust の Lambda ハンドラーには、厳密な命名制限はありません。ハンドラーには任意の名前を使用できますが、Rust の関数名は通常 `snake_case` です。

この例のような小規模なアプリケーションでは、1 つの `main.rs` ファイルを使用してすべてのコードを含めることができます。大規模なプロジェクトでは、`main.rs` に関数へのエントリポイントを含める必要がありますが、コードを論理モジュールに分離するための追加のファイルを含めることができます。例えば、次のような要件に応じて、ファイル構造ごとの証跡を作成できます。

```
/example-rust
│── src/
│   ├── main.rs        # Entry point
│   ├── handler.rs     # Contains main handler
│   ├── services.rs    # [Optional] Back-end service calls
│   ├── models.rs      # [Optional] Data models
│── Cargo.toml
```

## 入力イベントオブジェクトの定義とアクセス
<a name="rust-handler-input"></a>

JSON は Lambda 関数の最も一般的な標準入力形式です。この例では、関数は以下のような入力を想定しています:

```
{
    "order_id": "12345",
    "amount": 199.99,
    "item": "Wireless Headphones"
}
```

Rust では、構造体で予想される入力イベントの形状を定義できます。この例では、`Order` を表す以下の構造体を定義します。

```
#[derive(Deserialize, Serialize)]
struct Order {
    order_id: String,
    amount: f64,
    item: String,
}
```

この構造体は、予想される入力形状と一致します。この例では、`#[derive(Deserialize, Serialize)]` マクロはシリアル化と逆シリアル化のコードを自動的に生成します。つまり、`serde_json::from_value()` メソッドを使用して、汎用入力 JSON タイプを構造体に逆シリアル化できます。これはハンドラーの最初の数行に示されています:

```
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> {
    let payload = event.payload;

    // Deserialize the incoming event into Order struct
    let order: Order = serde_json::from_value(payload)?;
    ...
}
```

その後、オブジェクトのフィールドにアクセスできます。例えば、`order.order_id` は元の入力から `order_id` の値を取得します。

### 事前定義された入力イベントタイプ
<a name="rust-input-event-types"></a>

`aws_lambda_events` クレートには、多くの事前定義された入力イベントタイプがあります。例えば、API Gateway を使用して関数を呼び出す場合、次のインポートを含めます。

```
use aws_lambda_events::event::apigw::ApiGatewayProxyRequest;
```

次に、メインハンドラーが次の署名を使用していることを確認します。

```
async fn handler(event: LambdaEvent<ApiGatewayProxyRequest>) -> Result<String, Error> {
    let body = event.payload.body.unwrap_or_default();
    ...
}
```

その他の事前定義された入力イベントタイプの詳細については、[aws\$1lambda\$1events クレート](https://crates.io/crates/aws_lambda_events)を参照してください。

## Lambda コンテキストオブジェクトへのアクセスと使用
<a name="rust-example-context"></a>

Lambda [コンテキストオブジェクト](rust-context.md)には、呼び出し、関数、および実行環境に関する情報が含まれます。Rust では、`LambdaEvent` ラッパーにコンテキストオブジェクトが含まれます。例えば、コンテキストオブジェクトを使用して、次のコードで現在の呼び出しのリクエスト ID を取得できます。

```
async fn function_handler(event: LambdaEvent<Value>) -> Result<String, Error> {
    let request_id = event.context.request_id;
    ...
}
```

コンテキストオブジェクトの詳細については、「[Lambda コンテキストオブジェクトを使用して Rust 関数の情報を取得する](rust-context.md)」を参照してください。

## ハンドラーでの AWS SDK for Rust の使用
<a name="rust-example-sdk-usage"></a>

多くの場合、Lambda 関数を使用して、他の AWS リソースとやり取りしたり、更新したりします。これらのリソースとインターフェイスする最も簡単な方法は、[AWS SDK for Rust](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/welcome.html) を使用することです。

SDK 依存関係を関数に追加するには、`Cargo.toml` ファイルに追加します。関数に必要なライブラリのみを追加することをお勧めします。前のコード例では、`aws_sdk_s3::Client` を使用しました。`Cargo.toml` ファイルで、`[dependencies]` セクションの下に次の行を追加することで、この依存関係を追加できます。

```
aws-sdk-s3 = "1.78.0"
```

**注記**  
これは最新バージョンではない場合があります。アプリケーションに適したバージョンを選択します。

次に、依存関係をコードに直接インポートします。

```
use aws_sdk_s3::{Client, primitives::ByteStream};
```

次に、コード例は次のように Amazon S3 クライアントを初期化します。

```
let config = aws_config::load_defaults(aws_config::BehaviorVersion::latest()).await;
let s3_client = Client::new(&config);
```

SDK クライアントを初期化したら、SDK クライアントを使用して他の AWS サービスとやり取りできます。サンプルコードは、`upload_receipt_to_s3` ヘルパー関数で Amazon S3 `PutObject` API を呼び出します。

## 環境変数にアクセスする
<a name="rust-example-envvars"></a>

ハンドラーコードでは、`env::var` メソッドを使用して任意の[環境変数](configuration-envvars.md)を参照できます。この例では、以下のコード行を使用して、定義された `RECEIPT_BUCKET` 環境変数を参照します:

```
let bucket_name = env::var("RECEIPT_BUCKET")
    .map_err(|_| "RECEIPT_BUCKET environment variable is not set")?;
```

## 共有ステートを使用する
<a name="rust-shared-state"></a>

Lambda 関数のハンドラーコードとは共有された変数を宣言して変更することができます。これらの変数は、関数がイベントを受け取る前に、[初期化フェーズ](lambda-runtime-environment.md#runtimes-lifecycle-ib) 中の状態情報をロードするのに役立ちます。例えば、`main` 関数とハンドラー署名を更新することで、Amazon S3 クライアントを初期化するときに共有状態を使用するようにこのページのコードを変更することができます。

```
async fn function_handler(client: &Client, event: LambdaEvent<Value>) -> Result<String, Error> {
    ...
    upload_receipt_to_s3(client, &bucket_name, &key, &receipt_content).await?;
    ...
}

...
      
#[tokio::main]
async fn main() -> Result<(), Error> {
    let shared_config = aws_config::from_env().load().await;
    let client = Client::new(&shared_config);
    let shared_client = &client;
    lambda_runtime::run(service_fn(move |event: LambdaEvent<Request>| async move {
        handler(&shared_client, event).await
    }))
    .await
```

## Rust Lambda 関数のコードのベストプラクティス
<a name="rust-best-practices"></a>

Lambda 関数をビルドするときは、次のリストのガイドラインに従って、コーディングのベストプラクティスを実行してください。
+ **Lambda ハンドラーをコアロジックから分離します。**これにより、関数の単体テストが実行しやすくなります。
+ **依存関係の複雑さを最小限に抑えます。**フレームワークを単純化して、[実行環境](lambda-runtime-environment.md)起動時のロードを高速化します。
+ **デプロイパッケージをランタイムに必要な最小限のサイズにします。**これにより、呼び出しに先立ってデプロイパッケージをダウンロードして解凍する所要時間が短縮されます。

**実行環境の再利用を活用して関数のパフォーマンスを向上させます。**関数ハンドラー外で SDK クライアントとデータベース接続を初期化し、静的なアセットを `/tmp` ディレクトリにローカルにキャッシュします。関数の同じインスタンスで処理された後続の呼び出しは、これらのリソースを再利用できます。これにより、関数の実行時間が短縮され、コストが節約されます。

呼び出し間でデータが漏れるのを防ぐため、実行環境を使用してセキュリティ上の懸念があるユーザーデータ、イベント、またはその他の情報を保存しないでください。関数がハンドラー内のメモリに保存できない変更可能な状態に依存している場合は、ユーザーごとに個別の関数または個別のバージョンの関数を作成することを検討してください。

**keep-alive ディレクティブを使用して永続的な接続を維持します。**Lambda は、時間の経過とともにアイドル状態の接続を消去します。関数を呼び出すときにアイドル状態の接続を再利用しようとすると、接続エラーが発生します。永続的な接続を維持するには、ランタイムに関連付けられている keep-alive ディレクティブを使用します。例については、「[Node.js で Keep-alive を使用して接続を再利用する](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-reusing-connections.html)」を参照してください。

**[環境変数](configuration-envvars.md)を使用して、オペレーショナルパラメータを関数に渡します。**たとえば、Amazon S3 バケットに書き込む場合、書き込み先のバケット名はハードコーディングせずに、環境変数として設定します。

Lambda 関数では、**再帰呼び出しを使用しないでください**。関数が自身を呼び出すこともあれば、新たに開始されたプロセスで関数が再度呼び出される可能性もあります。これを行うと意図しないボリュームで関数が呼び出され、料金が急増する可能性があります。意図しない呼び出しがいくつも見つかった場合は、すぐに関数の予約済同時実行数を `0` に設定して、コードを更新している間のすべての関数の呼び出しをスロットリングします。

Lambda 関数コードで**文書化されていない非公開の API を使用しないでください**。AWS Lambda マネージドランタイムでは、Lambda が Lambda の内部 API にセキュリティと機能面の更新を定期的に適用します。これらの内部 API 更新には後方互換性がないことがあり、関数にこれらの非公開 API に対する依存関係がある場合、呼び出しの失敗などの意図しない結果につながります。公開されている API のリストについては、「[API リファレンス](https://docs.aws.amazon.com/lambda/latest/api/welcome.html)」を参照してください。

**冪等性コードを記述します。**関数の記述に冪等性コードを使用すると、重複するイベントが同じ方法で処理されるようになります。コードでは、イベントを適切に検証し、重複するイベントを適切に処理する必要があります。詳細については、「[Lambda 関数を冪等にするにはどうすればよいですか?](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-function-idempotent/)」を参照してください。

# Lambda コンテキストオブジェクトを使用して Rust 関数の情報を取得する
<a name="rust-context"></a>

Lambda で関数が実行されると、[ハンドラー](rust-handler.md)が受け取る LambdaEvent にコンテキストオブジェクトを追加します。このオブジェクトは、呼び出し、関数、および実行関数に関する情報を含むプロパティです。

**context プロパティ**
+  `request_id`: Lambda サービスによって生成された AWS リクエスト ID。
+  `deadline`: 現在の呼び出しの実行期限 (ミリ秒単位)。
+  `invoked_function_arn`: Lambda 関数の Amazon リソースネーム (ARN) が呼び出されます。
+  `xray_trace_id`: 現在の呼び出しの AWS X-Ray トレース ID。
+  `client_content`:AWS モバイル SDK によって送信されたクライアントコンテキストオブジェクト。AWS モバイル SDK を使用して関数を呼び出さない限り、このフィールドは空です。
+  `identity`: 関数を呼び出した Amazon Cognito ID。Lambda API への呼び出しリクエストが Amazon Cognito ID プールによって発行された AWS 認証情報を使用して行われた場合を除き、このフィールドは空です。
+  `env_config`: ローカル環境変数からの Lambda 関数の構成。このプロパティには、関数名、メモリ割り当て、バージョン、ログストリームなどの情報が含まれます。

## context の呼び出し情報へのアクセス
<a name="rust-context-invoke"></a>

Lambda 関数は、環境と呼び出しリクエストに関するメタデータにアクセスできます。関数ハンドラが受け取る `LambaEvent` オブジェクトには、`context` メタデータが含まれます。

```
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};

async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
    let invoked_function_arn = event.context.invoked_function_arn;
    Ok(json!({ "message": format!("Hello, this is function {invoked_function_arn}!") }))
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_runtime::run(service_fn(handler)).await
}
```

# Rust での HTTP イベントの処理
<a name="rust-http-events"></a>

Amazon API Gateway、アプリケーションロードバランサー、および [Lambda 関数 URL](urls-configuration.md) は、HTTP イベントを Lambda に送信できます。crates.io の [aws\$1lambda\$1events](https://crates.io/crates/aws_lambda_events) クレートを使用して、これらのソースからのイベントを処理できます。

**Example — API Gateway プロキシリクエストの処理**  
次の点に注意してください。  
+ `use aws_lambda_events::apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse}`: [aws\$1lambda\$1events](https://crates.io/crates/aws-lambda-events) クレートには、多くの Lambda イベントが含まれています。コンパイル時間を短縮するには、機能フラグを使用して必要なイベントをアクティブにします。例えば、`aws_lambda_events = { version = "0.8.3", default-features = false, features = ["apigw"] }` などです。
+ `use http::HeaderMap`: このインポートでは、依存関係に [http](https://crates.io/crates/http) クレートを追加する必要があります。

```
use aws_lambda_events::apigw::{ApiGatewayProxyRequest, ApiGatewayProxyResponse};
use http::HeaderMap;
use lambda_runtime::{service_fn, Error, LambdaEvent};

async fn handler(
    _event: LambdaEvent<ApiGatewayProxyRequest>,
) -> Result<ApiGatewayProxyResponse, Error> {
    let mut headers = HeaderMap::new();
    headers.insert("content-type", "text/html".parse().unwrap());
    let resp = ApiGatewayProxyResponse {
        status_code: 200,
        multi_value_headers: headers.clone(),
        is_base64_encoded: false,
        body: Some("Hello AWS Lambda HTTP request".into()),
        headers,
    };
    Ok(resp)
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_runtime::run(service_fn(handler)).await
}
```

また、[Lambda 用の Rust ランタイムクライアント](https://github.com/aws/aws-lambda-rust-runtime)では、これらのイベントタイプを抽象化することもでき、どのサービスがイベントを送信するかに関係なく、ネイティブ HTTP タイプを使用できます。次のコードは前の例と同等で、Lambda 関数 URL、Application Load Balancer、API Gateway ですぐに使用できます。

**注記**  
[lambda\$1http](https://crates.io/crates/lambda_http) クレートは、その下の [lambda\$1runtime](https://crates.io/crates/lambda_runtime) クレートを使用します。`lambda_runtime` を個別にインポートする必要はありません。

**Example — HTTP リクエストの処理**  

```
use lambda_http::{service_fn, Error, IntoResponse, Request, RequestExt, Response};

async fn handler(event: Request) -> Result<impl IntoResponse, Error> {
    let resp = Response::builder()
        .status(200)
        .header("content-type", "text/html")
        .body("Hello AWS Lambda HTTP request")
        .map_err(Box::new)?;
    Ok(resp)
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_http::run(service_fn(handler)).await
}
```

`lambda_http` の使用方法の別の例については、AWS Labs GitHub リポジトリにある「[http-axum コードサンプル](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/http-axum/src/main.rs)」を参照してください。

**Rust 用サンプル HTTP Lambda イベント**
+ 「[Lambda HTTP イベント](https://github.com/aws/aws-lambda-rust-runtime/tree/main/examples/http-basic-lambda)」: HTTP イベントを処理する Rust 関数。
+ 「[CORS ヘッダー付き Lambda HTTP イベント](https://github.com/aws/aws-lambda-rust-runtime/blob/main/examples/http-cors)」: Tower を使用して CORS ヘッダーを挿入する Rust 関数。
+ [共有リソースを使用する Lambda HTTP イベント](https://github.com/aws/aws-lambda-rust-runtime/tree/main/examples/basic-shared-resource): 関数ハンドラーが作成される前に初期化された共有リソースを使用する Rust 関数。

# .zip ファイルアーカイブを使用して Rust Lambda 関数をデプロイする
<a name="rust-package"></a>

このページでは、Rust 関数をコンパイルし、[Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html) を使用して コンパイルしたバイナリを AWS Lambda にデプロイする方法について説明します。また、AWS Command Line Interface と AWS Serverless Application Model CLI を使用してコンパイルしたバイナリをデプロイする方法も示します。

**Topics**
+ [

## 前提条件
](#rust-package-prerequisites)
+ [

## MacOS、Windows、Linux で Rust 関数をビルドする
](#rust-package-build)
+ [

## Cargo Lambda による Rust 関数バイナリのデプロイ
](#rust-deploy-cargo)
+ [

## Cargo Lambda を使用して Rust 関数を呼び出す
](#rust-invoke-function)

## 前提条件
<a name="rust-package-prerequisites"></a>
+ [Rust](https://www.rust-lang.org/tools/install)
+ [AWS CLI バージョン 2](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)

## MacOS、Windows、Linux で Rust 関数をビルドする
<a name="rust-package-build"></a>

次の手順では、Rust を使用して最初の Lambda 関数のプロジェクトを作成し、[Cargo Lambda](https://www.cargo-lambda.info/) を使用してコンパイルする方法を示します。Cargo Lambda は、Rust Lambda 関数の構築とデプロイを簡素化する Cargo コマンドラインツールのサードパーティーのオープンソース拡張機能です。

1. Rust Lambda 関数の構築とデプロイを簡素化する Cargo コマンドラインツールのサードパーティーのオープンソース拡張である [Cargo Lambda](https://www.cargo-lambda.info/guide/what-is-cargo-lambda.html) をインストールします。

   ```
   cargo install cargo-lambda
   ```

   その他のインストールオプションについては、Cargo Lambda ドキュメントの「[インストール](https://www.cargo-lambda.info/guide/installation.html)」を参照してください。

1. パッケージ構造を作成します。このコマンドは、`src/main.rs` に基本的な関数コードを作成します。このコードをテストに使用することも、独自のコードで置き換えることもできます。

   ```
   cargo lambda new my-function
   ```

1. パッケージのルートディレクトリ内で [build](https://www.cargo-lambda.info/commands/build.html) サブコマンドを実行して、関数内のコードをコンパイルします。

   ```
   cargo lambda build --release
   ```

   (オプション) Lambda でAWS Graviton2 を使用する場合は、`--arm64` フラグを追加して ARM CPU 用のコードをコンパイルします。

   ```
   cargo lambda build --release --arm64
   ```

1. Rust 関数をデプロイする前に、マシンに AWS 認証情報を設定します。

   ```
   aws configure
   ```

## Cargo Lambda による Rust 関数バイナリのデプロイ
<a name="rust-deploy-cargo"></a>

[deploy](https://www.cargo-lambda.info/commands/deploy.html) サブコマンドを使用して、コンパイルされたバイナリを Lambda にデプロイします。このコマンドは[実行ロール](lambda-intro-execution-role.md)を作成し、次に Lambda 関数を作成します。既存の実行ロールを指定するには、[--iam-role フラグを使用します](https://www.cargo-lambda.info/commands/deploy.html#iam-roles)。

```
cargo lambda deploy my-function
```

### AWS CLI を使用して Rust 関数バイナリをデプロイする
<a name="rust-deploy-aws-cli"></a>

AWS CLI を使用してバイナリをデプロイすることもできます。

1. .zip デプロイパッケージをビルドするには、[ビルド](https://www.cargo-lambda.info/commands/build.html)サブコマンドを使用します。

   ```
   cargo lambda build --release --output-format zip
   ```

1. .zip パッケージをデプロイするには、[create-function](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lambda/create-function.html) コマンドを実行します。
   + `--runtime` の場合、`provided.al2023` を指定します。これは [OS のみのランタイム](runtimes-provided.md)です。OS のみのランタイムは、コンパイルされたバイナリとカスタムランタイムを Lambda にデプロイするために使用されます。
   + `--role` には、[実行ロール](lambda-intro-execution-role.md)の ARN を指定します。

   ```
   aws lambda create-function \
        --function-name my-function \
        --runtime provided.al2023 \
        --role arn:aws:iam::111122223333:role/lambda-role \
        --handler rust.handler \
        --zip-file fileb://target/lambda/my-function/bootstrap.zip
   ```

### AWS SAM CLI による Rust 関数バイナリのデプロイ
<a name="rust-deploy-sam-cli"></a>

AWS SAM CLI を使用してバイナリをデプロイすることもできます。

1. リソースとプロパティの定義を含む AWS SAM テンプレートを作成します。`Runtime` の場合、`provided.al2023` を指定します。これは [OS のみのランタイム](runtimes-provided.md)です。OS のみのランタイムは、コンパイルされたバイナリとカスタムランタイムを Lambda にデプロイするために使用されます。

   AWS SAM を使用して Lambda 関数をデプロイする方法の詳細については、「*AWS Serverless Application Model 開発者ガイド*」の「[AWS::Serverless::Function](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html)」を参照してください。  
**Example Rust バイナリの SAM リソースとプロパティ定義**  

   ```
   AWSTemplateFormatVersion: '2010-09-09'
   Transform: AWS::Serverless-2016-10-31
   Description: SAM template for Rust binaries
   Resources:
     RustFunction:
       Type: AWS::Serverless::Function 
       Properties:
         CodeUri: target/lambda/my-function/
         Handler: rust.handler
         Runtime: provided.al2023
   Outputs:
     RustFunction:
       Description: "Lambda Function ARN"
       Value: !GetAtt RustFunction.Arn
   ```

1. [build](https://www.cargo-lambda.info/commands/build.html) サブコマンドを使用して関数をコンパイルします。

   ```
   cargo lambda build --release
   ```

1. [sam deploy](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html) コマンドを使用して、関数を Lambda にデプロイします。

   ```
   sam deploy --guided
   ```

AWS SAM CLI を使用した Rust 関数をビルドする方法の詳細については、「*AWS Serverless Application Model デベロッパーガイド*」の「[Cargo Lambda を使用した Rust Lambda 関数の構築](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/building-rust.html)」を参照してください。

## Cargo Lambda を使用して Rust 関数を呼び出す
<a name="rust-invoke-function"></a>

[invoke](https://www.cargo-lambda.info/commands/invoke.html) サブコマンドを使用して、ペイロードを用いて関数をテストします。

```
cargo lambda invoke --remote --data-ascii '{"command": "Hello world"}' my-function
```

### AWS CLI を使用して Rust 関数を呼び出すには
<a name="rust-invoke-cli"></a>

また、AWS CLI を使用して関数を呼び出すこともできます。

```
aws lambda invoke --function-name my-function --cli-binary-format raw-in-base64-out --payload '{"command": "Hello world"}' /tmp/out.txt
```

AWS CLI バージョン 2 を使用している場合、**cli-binary-format** オプションは必須です。これをデフォルト設定にするには、`aws configure set cli-binary-format raw-in-base64-out` を実行します。詳細については、「*AWS Command Line Interface バージョン 2 用ユーザーガイド*」の「[AWS CLI でサポートされているグローバルコマンドラインオプション](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-options.html#cli-configure-options-list)」を参照してください。

# Rust Lambda 関数のレイヤーを操作する
<a name="rust-layers"></a>

Rust で書かれた Lambda 関数の依存関係を[レイヤー](chapter-layers.md)で管理する方法はお勧めしません。Rust の Lambda 関数は単一の実行可能ファイルにコンパイルされ、関数をデプロイしたときにそのファイルが Lambda に渡されるためです。この実行可能ファイルには、コンパイルされた関数コードがそのすべての依存関係と共に含まれています。レイヤーを使用すると、このプロセスが複雑になります。そのうえ、他に必要なアセンブリがあれば、初期化フェーズ中にいちいちメモリにロードしなければならないため、コールドスタート時間が長くなります。

Rust ハンドラーで外部の依存関係を使用するには、その依存関係をデプロイパッケージに直接含めます。デプロイプロセスが簡素化されるうえ、ビルトインの Rust コンパイラの最適化機能を活用できます。AWS SDK for Rust のような依存関係を関数にインポートして使用する方法の例については、「[Rust の Lambda 関数ハンドラーの定義](rust-handler.md)」を参照してください。

# Rust Lambda 関数のログ記録とモニタリング
<a name="rust-logging"></a>

AWS Lambda は、ユーザーに代わって Lambda 関数を自動的にモニタリングし、Amazon CloudWatch にログを送信します。Lambda 関数には、関数のインスタンスごとに CloudWatch Logs ロググループとログストリームが用意されています。Lambda ランタイム環境は、各呼び出しの詳細をログストリームに送信し、関数のコードからのログやその他の出力を中継します。詳細については、「[Lambda 関数ログを CloudWatch Logs に送信する](monitoring-cloudwatchlogs.md)」を参照してください。ログの形式の詳細については、「[JSON とプレーンテキストのログフォーマットの設定](monitoring-cloudwatchlogs-logformat.md)」を参照してください。このページでは、Lambda 関数のコードからログ出力を生成する方法について説明します。

## ログを書く関数の作成
<a name="rust-logging-function"></a>

関数コードからログを出力するには、`println!` マクロなどの `stdout` または `stderr` に書き込む任意のログ記録ライブラリを使用できます。次の例は、`println!`を使用して、関数ハンドラーの開始時と終了前にメッセージを印刷します。

```
use lambda_runtime::{service_fn, LambdaEvent, Error};
use serde_json::{json, Value};
async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
    println!("Rust function invoked");
    let payload = event.payload;
    let first_name = payload["firstName"].as_str().unwrap_or("world");
    println!("Rust function responds to {}", &first_name);
    Ok(json!({ "message": format!("Hello, {first_name}!") }))
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    lambda_runtime::run(service_fn(handler)).await
}
```

## トレーシングクレートによる高度なログ記録機能の実装
<a name="rust-logging-tracing"></a>

[トレーシング](https://crates.io/crates/tracing)は、構造化されたイベントベースの診断情報を収集するために Rust プログラムをインストルメントするためのフレームワークです。このフレームワークには、構造化された JSON ログメッセージの作成など、ロギング出力レベルと形式をカスタマイズするユーティリティが用意されています。このフレームワークを使用するには、関数ハンドラーを実装する前に `subscriber` を初期化する必要があります。次に、`debug`、`info`、`error` などのトレースマクロを使用して、各シナリオに必要なログのレベルを指定できます。

**Example — トレーシングクレートの使用**  
次の点に注意してください。  
+ `tracing_subscriber::fmt().json()`: このオプションが含まれる場合、ログは JSON でフォーマットされます。このオプションを使用するには、`tracing-subscriber` の `json` 機能を依存関係に含める必要があります (例: `tracing-subscriber = { version = "0.3.11", features = ["json"] }`)。
+ `#[tracing::instrument(skip(event), fields(req_id = %event.context.request_id))]`: このアノテーションは、ハンドラが呼び出されるたびにスパンを生成します。このスパンは、各ログ行にリクエスト ID を追加します。
+ `{ %first_name }`: このコンストラクトは、使用されているログ行に `first_name` フィールドを追加します。このフィールドの値は、同じ名前の変数に対応します。

```
use lambda_runtime::{service_fn, Error, LambdaEvent};
use serde_json::{json, Value};
#[tracing::instrument(skip(event), fields(req_id = %event.context.request_id))]
async fn handler(event: LambdaEvent<Value>) -> Result<Value, Error> {
    tracing::info!("Rust function invoked");
    let payload = event.payload;
    let first_name = payload["firstName"].as_str().unwrap_or("world");
    tracing::info!({ %first_name }, "Rust function responds to event");
    Ok(json!({ "message": format!("Hello, {first_name}!") }))
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt().json()
        .with_max_level(tracing::Level::INFO)
        // this needs to be set to remove duplicated information in the log.
        .with_current_span(false)
        // this needs to be set to false, otherwise ANSI color codes will
        // show up in a confusing manner in CloudWatch logs.
        .with_ansi(false)
        // disabling time is handy because CloudWatch will add the ingestion time.
        .without_time()
        // remove the name of the function from every log entry
        .with_target(false)
        .init();
    lambda_runtime::run(service_fn(handler)).await
}
```

この Rust 関数が呼び出されると、次のようなログ行が 2 行出力されます。

```
{"level":"INFO","fields":{"message":"Rust function invoked"},"spans":[{"req_id":"45daaaa7-1a72-470c-9a62-e79860044bb5","name":"handler"}]}
{"level":"INFO","fields":{"message":"Rust function responds to event","first_name":"David"},"spans":[{"req_id":"45daaaa7-1a72-470c-9a62-e79860044bb5","name":"handler"}]}
```