Skip to content

Commit

Permalink
WIP basic1
Browse files Browse the repository at this point in the history
  • Loading branch information
kenken714 committed Oct 16, 2024
1 parent 99a0921 commit d181e8f
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 139 deletions.
2 changes: 1 addition & 1 deletion docs/chapter1/section4/0_prepare.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## リポジトリの準備

なろう講習会のバックエンド用のテンプレートリポジトリ[https://github.com/traPtitech/naro-template-backend](https://github.com/traPtitech/naro-template-backend) を使います。リンクから GitHub のリポジトリにアクセスしてください。右上の「Use this template」と書かれたボタンをクリックし、「Create a new repository」から自分のアカウントにリポジトリを作ります。リポジトリ名は`naro-backend`など適当なものにしましょう。
なろう講習会 in Rust のバックエンド用のテンプレートリポジトリ[https://github.com/traP-jp/naro-rs-template-backend](https://github.com/traP-jp/naro-rs-template-backend) を使います。リンクから GitHub のリポジトリにアクセスしてください。右上の「Use this template」と書かれたボタンをクリックし、「Create a new repository」から自分のアカウントにリポジトリを作ります。リポジトリ名は`naro-rs-backend`など適当なものにしましょう。

![](images/template.png)

Expand Down
31 changes: 15 additions & 16 deletions docs/chapter1/section4/3_rust_and_db.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
# Goでデータベースを扱う
# Rustでデータベースを扱う

ここからは Go でプログラムを書いてデータベースを扱っていきます。`task up`を実行してデータベースが立ち上がっていることを確認してください。
まずは VSCode で先ほどクローンしてきたリポジトリを開きましょう。画像のようなファイルが入っているはずです。 main.go を開いてください。
ここからは Rust でプログラムを書いてデータベースを扱っていきます。`task up`を実行してデータベースが立ち上がっていることを確認してください。
まずは VSCode で先ほどクローンしてきたリポジトリを開きましょう。画像のようなファイルが入っているはずです。 main.rs を開いてください。
![](images/files.png)

## データベースに接続する

### Goのプログラムを書く
### Rustのプログラムを書く

サンプルのプログラムが書いてありますが、データベースと接続できるように書き換えます。
Go でデータベースに接続するためのライブラリは様々ありますが、今回は SQL 文を書く sqlx を使います。
Rust でデータベースに接続するためのライブラリは様々ありますが、今回は SQL 文を書く SQLx を使います。

- 参考
- [jmoiron/sqlx: a set of extensions on go's standard `database/sql` library.](https://pkg.go.dev/github.com/jmoiron/sqlx)
- [Illustrated guide to SQLX](https://jmoiron.github.io/sqlx/)
- [launchbadge/sqlx: 🧰 The Rust SQL Toolkit](https://github.com/launchbadge/sqlx)
- [sqlx - Rust](https://docs.rs/sqlx/latest/sqlx/)

<<< @/chapter1/section4/src/connect_db.go{go:line-numbers}
<<< @/chapter1/section4/src/connect_db.rs{rust:line-numbers}

`// #region`などのコメントは無視してください。

書き換えた後、 import の周りで赤字のエラーが出た場合は、ターミナルで`go mod tidy`を実行してください。
26 から 40 行目でデータベースに接続するための設定をして、42 行目の`db, err := sqlx.Open("mysql", conf.FormatDSN())`でデータベースに接続しています。32 行目などで`os.Getenv()`という関数が出てきていますが、これは環境変数と呼ばれる、コンピューター側で設定してプログラムで使えるようにしている変数です。今は必要なデータベースのパスワードなどの環境変数を何も設定していないので、設定します。
Expand Down Expand Up @@ -71,7 +70,7 @@ $ source .env
### 実行する

```sh
$ go run main.go
$ cargo run
```

出力はこのようになります。
Expand All @@ -81,30 +80,30 @@ connected
Tokyoの人口は7980230人です
```

`main.go`を解説してきます。
`main.rs`を解説してきます。

<<< @/chapter1/section4/src/connect_db.go#city
<<< @/chapter1/section4/src/connect_db.rs#city

この`City`構造体の横にあるバッククオートで囲まれたタグに`db`でデータベースのカラム名を指定します。これによってライブラリがデータベースから取得したレコードを構造体に上手くあてはめてくれます。

参考: [Struct タグについて | text.Baldanders.info](https://text.baldanders.info/golang/struct-tag/)

<<< @/chapter1/section4/src/connect_db.go#get
<<< @/chapter1/section4/src/connect_db.rs#get

`City`型の`city`という変数のポインタを sqlx ライブラリの`Get`関数の第 1 引数に指定します。第 2 引数には SQL 文を書きます。`Name = ?`としていますが、第 3 引数以降の値が順番に`?`へと当てはめられて SQL 文が実行され、取得したレコードが`city`変数に代入されます。

### 基本問題

```sh
$ go run main.go {都市の名前}
$ cargo run {都市の名前}
```

と入力して、同様に人口を表示するようにしましょう。

ヒント:[Go言語 - os.Argsでコマンドラインパラメータを受け取る - 覚えたら書く](https://blog.y-yuki.net/entry/2017/04/30/000000)
ヒント:[コマンドライン引数を受け付ける - The Rust Programming Language 日本語版](https://doc.rust-jp.rs/book-ja/ch12-01-accepting-command-line-arguments.html)

:::details 答え
<<< @/chapter1/section4/src/practice_basic1.go
<<< @/chapter1/section4/src/practice_basic1.rs
:::

### 応用問題
Expand Down
Binary file modified docs/chapter1/section4/images/files.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 0 additions & 60 deletions docs/chapter1/section4/src/connect_db.go

This file was deleted.

56 changes: 56 additions & 0 deletions docs/chapter1/section4/src/connect_db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use anyhow::Ok;
use sqlx::mysql::MySqlConnectOptions;
use std::env;

// #region city
#[derive(sqlx::FromRow)]
#[sqlx(rename_all = "PascalCase")]
#[allow(dead_code)] // 使用していないフィールドへの警告を抑制
struct City {
#[sqlx(rename = "ID")]
id: i32,
name: String,
country_code: String,
district: String,
population: i32,
}
// #endregion city

fn get_option() -> anyhow::Result<MySqlConnectOptions> {
let host = env::var("DB_HOSTNAME")?;
let port = env::var("DB_PORT")?.parse()?;
let username = env::var("DB_USERNAME")?;
let password = env::var("DB_PASSWORD")?;
let database = env::var("DB_DATABASE")?;
let timezone = Some(String::from("Asia/Tokyo"));
let collation = String::from("utf8mb4_unicode_ci");

Ok(MySqlConnectOptions::new()
.host(&host)
.port(port)
.username(&username)
.password(&password)
.database(&database)
.timezone(timezone)
.collation(&collation))
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let options = get_option()?;
let pool = sqlx::MySqlPool::connect_with(options).await?;

println!("Connected");
// #region get
let city = sqlx::query_as::<_, City>("SELECT * FROM city WHERE Name = ?")
.bind("Tokyo")
.fetch_one(&pool)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => anyhow::anyhow!("no such city Name = {}\n", "Tokyo"),
_ => anyhow::anyhow!("DB error: {}", e),
})?;
// #endregion get
println!("Tokyoの人口は{}人です", &city.population);
Ok(())
}
62 changes: 0 additions & 62 deletions docs/chapter1/section4/src/practice_basic1.go

This file was deleted.

60 changes: 60 additions & 0 deletions docs/chapter1/section4/src/practice_basic1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use anyhow::Ok;
use sqlx::mysql::MySqlConnectOptions;
use std::env;

// #region city
#[derive(sqlx::FromRow)]
#[sqlx(rename_all = "PascalCase")]
#[allow(dead_code)] // 使用していないフィールドへの警告を抑制
struct City {
#[sqlx(rename = "ID")]
id: i32,
name: String,
country_code: String,
district: String,
population: i32,
}
// #endregion city

fn get_option() -> anyhow::Result<MySqlConnectOptions> {
let host = env::var("DB_HOSTNAME")?;
let port = env::var("DB_PORT")?.parse()?;
let username = env::var("DB_USERNAME")?;
let password = env::var("DB_PASSWORD")?;
let database = env::var("DB_DATABASE")?;
let timezone = Some(String::from("Asia/Tokyo"));
let collation = String::from("utf8mb4_unicode_ci");

Ok(MySqlConnectOptions::new()
.host(&host)
.port(port)
.username(&username)
.password(&password)
.database(&database)
.timezone(timezone)
.collation(&collation))
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let options = get_option()?;
let pool = sqlx::MySqlPool::connect_with(options).await?;

let city_name = env::args().nth(1).expect("city name is required"); // [!code ++]
println!("Connected");
// #region get
let city = sqlx::query_as::<_, City>("SELECT * FROM city WHERE Name = ?")
.bind("Tokyo") // [!code --]
.bind(&city_name) // [!code ++]
.fetch_one(&pool)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => anyhow::anyhow!("no such city Name = {}\n", "Tokyo"), // [!code --]
sqlx::Error::RowNotFound => anyhow::anyhow!("no such city Name = {}\n", &city_name), // [!code ++]
_ => anyhow::anyhow!("DB error: {}", e),
})?;
// #endregion get
println!("Tokyoの人口は{}人です", &city.population); // [!code --]
println!("{}の人口は{}人です", &city_name, &city.population); // [!code ++]
Ok(())
}

0 comments on commit d181e8f

Please sign in to comment.