1️⃣
AWS SDK for Rust v1 で DynamoDB を操作する
AWS SDK for Rust が GA
AWS re:Invent 2023 に合わせて、AWS SDK for Rust が GA されましました。
Rustacean にとって、待望の GA ではないでしょうか🥳🎉
Getting Started with AWS SDK for Rust
過去に AWS SDK for Rust の Developer Preview が発表された際に試したものを
正式リリース版で試してみたいと思います。
- AWS SDK for RustでDynamoDBのデータをScanする
- AWS SDK for RustでDynamoDBのデータをQuery検索する
- AWS SDK for RustでDynamoDBへのCRUDを実装する
なお、公式のサンプルコードは examples にあります。
事前準備
DynamoDB に Users テーブルを作成し、「スラムダンク 湘北高校のメンバー」を登録します。
- テーブル名:Users
- パーティションキー:Id(Number)
- ソートキー:なし
サンプルデータ:スラムダンク 湘北高校のメンバー
{
"users": [
{
"Id": 1,
"Name": "桜木花道",
"Grade": 1,
"Position": "PF",
"Number": 10,
"Height": 189,
"Weight": 83
},
{
"Id": 2,
"Name": "流川楓",
"Grade": 1,
"Position": "SF",
"Number": 11,
"Height": 187,
"Weight": 75
},
{
"Id": 3,
"Name": "赤木剛憲",
"Grade": 3,
"Position": "C",
"Number": 4,
"Height": 197,
"Weight": 93
},
{
"Id": 4,
"Name": "宮城リョータ",
"Grade": 2,
"Position": "PG",
"Number": 7,
"Height": 168,
"Weight": 59
},
{
"Id": 5,
"Name": "三井寿",
"Grade": 3,
"Position": "SG",
"Number": 14,
"Height": 184,
"Weight": 70
},
{
"Id": 6,
"Name": "木暮公延",
"Grade": 3,
"Position": "SF",
"Number": 5,
"Height": 178,
"Weight": 62
}
]
}
また、Rust の実行環境に以下の環境変数をセットします。
- AWS_DEFAULT_REGION
- AWS_SECRET_ACCESS_KEY
- AWS_ACCESS_KEY_ID
Cargo.toml
Cargo.toml
に SDK を追加します。
Cargo.toml
[dependencies]
aws-config = { version = "1", features = ["behavior-version-latest"] }
aws-sdk-dynamodb = "1"
tokio = { version = "1", features = ["full"] }
DynamoDB のデータを Scan する
過去のコードをそのまま実行することができました!
main.rs
use aws_sdk_dynamodb::{Client};
#[tokio::main]
async fn main() {
let config = aws_config::load_from_env().await;
let client = Client::new(&config);
let resp = client.scan().table_name("Users").send().await;
match resp {
Ok(scan_output) => {
if let Some(items) = scan_output.items {
for item in items {
println!("{:?}", item);
// Idを取り出す
if let Some(attr_val) = item.get("Id") {
if let Ok(id_val) = attr_val.as_n() {
if let Ok(id) = id_val.parse::<u32>() {
println!("{}", id);
}
}
}
// Nameを取り出す
if let Some(attr_val) = item.get("Name") {
if let Ok(name) = attr_val.as_s() {
println!("{}", name.to_string());
}
}
}
}
},
Err(e) => {
println!("{}", e);
}
}
}
実行結果
{"Id": N("3"), "Weight": N("93"), "Height": N("197"), "Name": S("赤木剛憲"), "Number": N("4"), "Position": S("C"), "Grade": N("3")}
3
赤木剛憲
{"Id": N("2"), "Number": N("11"), "Height": N("187"), "Name": S("流川楓"), "Position": S("SF"), "Grade": N("1"), "Weight": N("75")}
2
流川楓
{"Number": N("7"), "Grade": N("2"), "Weight": N("59"), "Id": N("4"), "Name": S("宮城リョータ"), "Position": S("PG"), "Height": N("168")}
4
宮城リョータ
{"Grade": N("3"), "Name": S("木暮公延"), "Height": N("178"), "Id": N("6"), "Number": N("5"), "Position": S("SF"), "Weight": N("62")}
6
木暮公延
{"Position": S("PF"), "Height": N("189"), "Id": N("1"), "Name": S("桜木花道"), "Number": N("10"), "Grade": N("1"), "Weight": N("83")}
1
桜木花道
{"Height": N("184"), "Position": S("SG"), "Grade": N("3"), "Id": N("5"), "Number": N("14"), "Name": S("三井寿"), "Weight": N("70")}
5
三井寿
DynamoDB のデータを Query 検索する
修正が必要な箇所は 1 箇所でした。
model
が types
に変更されたようです。
main.rd
use aws_sdk_dynamodb::{Client};
- use aws_sdk_dynamodb::model::{AttributeValue};
+ use aws_sdk_dynamodb::types::AttributeValue;
#[tokio::main]
async fn main() {
let config = aws_config::load_from_env().await;
let client = Client::new(&config);
let resp = client
.query()
.table_name("Users")
.key_condition_expression("#key = :value".to_string())
.expression_attribute_names("#key".to_string(), "Id".to_string())
.expression_attribute_values(":value".to_string(), AttributeValue::N("1".to_string()))
.send()
.await;
match resp {
Ok(query_output) => {
if let Some(items) = query_output.items {
for item in items {
println!("{:?}", item);
// Idを取り出す
if let Some(attr_val) = item.get("Id") {
if let Ok(id_val) = attr_val.as_n() {
if let Ok(id) = id_val.parse::<u32>() {
println!("{}", id);
}
}
}
// Nameを取り出す
if let Some(attr_val) = item.get("Name") {
if let Ok(name) = attr_val.as_s() {
println!("{}", name.to_string());
}
}
}
}
}
Err(e) => {
println!("{}", e);
}
}
}
実行結果
{"Grade": N("1"), "Position": S("PF"), "Number": N("10"), "Height": N("189"), "Id": N("1"), "Weight": N("83"), "Name": S("桜木花道")}
1
桜木花道
DynamoDB のデータの CRUD(PutItem, GetItem, UpdateItem, DeleteItem) を実装する
上記と同じで修正が必要な箇所は 1 箇所で、model
が types
に変更されたようです。
main.rs
use aws_sdk_dynamodb::{Client};
- use aws_sdk_dynamodb::model::{AttributeValue, ReturnValue, AttributeValueUpdate, AttributeAction};
+ use aws_sdk_dynamodb::types::{AttributeAction, AttributeValue, AttributeValueUpdate, ReturnValue};
#[tokio::main]
async fn main() {
let config = aws_config::load_from_env().await;
let client = Client::new(&config);
let user = User {
id: 7,
name: "仙道彰".to_string(),
grade: 2,
position: "SF".to_string(),
number: 7,
height: 190,
weight: 79
};
// データ登録
if create(&client, &user).await {
println!("Create 完了");
}
// データ取得
if read(&client, &user.id.to_string()).await {
println!("Read 完了");
}
// データ更新
let target_id = "7".to_string();
let new_position = "SF/PG".to_string();
if update(&client, &target_id, &new_position).await {
println!("Update 完了");
}
// データ削除
if delete(&client, &user.id.to_string()).await {
println!("Delete 完了");
}
}
struct User {
id: u32,
name: String,
grade: u32,
position: String,
number: u32,
height: u32,
weight: u32
}
async fn create(client: &Client, user: &User) -> bool {
match client
.put_item()
.table_name("Users")
.item("Id", AttributeValue::N(user.id.to_string()))
.item("Name", AttributeValue::S(user.name.to_string()))
.item("Grade", AttributeValue::N(user.grade.to_string()))
.item("Position", AttributeValue::S(user.position.to_string()))
.item("Number", AttributeValue::N(user.number.to_string()))
.item("Height", AttributeValue::N(user.height.to_string()))
.item("Weight", AttributeValue::N(user.weight.to_string()))
.return_values(ReturnValue::AllOld)
.send()
.await
{
Ok(put_item_output) => {
println!("{:?}", put_item_output);
true
},
Err(e) => {
println!("{}", e);
false
}
}
}
async fn read(client: &Client, id: &String) -> bool {
match client
.get_item()
.table_name("Users")
.key("Id".to_string(), AttributeValue::N(id.to_string()))
.send()
.await {
Ok(get_item_output) => {
if let Some(item) = get_item_output.item {
println!("{:?}", item);
// Idを取り出す
if let Some(attr_val) = item.get("Id") {
if let Ok(id_val) = attr_val.as_n() {
if let Ok(id) = id_val.parse::<u32>() {
println!("{}", id);
}
}
}
// Nameを取り出す
if let Some(attr_val) = item.get("Name") {
if let Ok(name) = attr_val.as_s() {
println!("{}", name.to_string());
}
}
}
true
}
Err(e) => {
println!("{}", e);
false
}
}
}
async fn update (client: &Client, id: &String, position: &String) -> bool {
let attr_val_up = AttributeValueUpdate::builder()
.action(AttributeAction::Put)
.value(AttributeValue::S(position.to_string()))
.build();
match client
.update_item()
.table_name("Users")
.key("Id", AttributeValue::N(id.to_string()))
.attribute_updates("Position", attr_val_up)
.return_values(ReturnValue::AllNew)
.send()
.await
{
Ok(put_item_output) => {
println!("{:?}", put_item_output);
true
},
Err(e) => {
println!("{}", e);
false
}
}
}
async fn delete(client: &Client, id: &String) -> bool {
match client
.delete_item()
.table_name("Users")
.key("Id", AttributeValue::N(id.to_string()))
.send()
.await
{
Ok(delete_item_output) => {
println!("{:?}", delete_item_output);
true
},
Err(e) => {
println!("{}", e);
false
}
}
}
実行結果
PutItemOutput { attributes: None, consumed_capacity: None, item_collection_metrics: None, _request_id: Some("MB70NB9IRSBNVMNLM7NJUQJV6NVV4KQNSO5AEMVJF66Q9ASUAAJG") }
Create 完了
{"Position": S("SF"), "Weight": N("79"), "Height": N("190"), "Id": N("7"), "Number": N("7"), "Name": S("仙道彰"), "Grade": N("2")}
7
仙道彰
Read 完了
UpdateItemOutput { attributes: Some({"Name": S("仙道彰"), "Id": N("7"), "Height": N("190"), "Grade": N("2"), "Number": N("7"), "Weight": N("79"), "Position": S("SF/PG")}), consumed_capacity: None, item_collection_metrics: None, _request_id: Some("ATMP9H54EGPE4EL2NCO5R66IHFVV4KQNSO5AEMVJF66Q9ASUAAJG") }
Update 完了
DeleteItemOutput { attributes: None, consumed_capacity: None, item_collection_metrics: None, _request_id: Some("K5H5BO3OBONSVBP8J6HR0J3GM3VV4KQNSO5AEMVJF66Q9ASUAAJG") }
Delete 完了
まとめ
Developer Preview v0.2.0 で実装した DynamoDB 関連のコードがほぼ修正なしに実行できました。
SDK が GA されたことで、Rust での開発も活発になると良いですね🙌
Discussion