Rust 学ぶ
Rust の基本構文は、昔に少しだけ学んだ。今は完全に忘れている。
とりあえず今回はアプリを作りながら学んでみる。
適当に Web フレームワークを選定。Rocket にしてみる。理由は特にない。どういう素性なんだろう。
Rust は asdf で管理しているので最新にしてみる。v1.70.0 をインストール。
asdf install rust 1.70.0
asdf global rust 1.70.0
チュートリアルを見ると Shuttle というサービスを使って Rust アプリを動かすことができるらしい。Ruby でいうところの Heroku、Next でいうところの Vercel だろうか。
Shuttle を CLI 操作?できるライブラリをインストールしてみる。
cargo install cargo-shuttle
Rust のバージョンが古いと (1.69.0) 普通にエラーになる。
error[E0658]: use of unstable library feature 'is_some_and'
is_some_and
は 1.70.0 から追加されたらしい。
Rust のライブラリバージョン依存ってどういう仕組みなんだろ。インストールされている Rust バージョンに合わせて cargo が適切なバージョンをインストールしてくれるわけじゃないのか?
とりあえず無事に cargo-shuttle インストールできた。PATH に追加しろというので fish の PATH 追加をする。
fish_add_path /Users/zaru/.cargo/bin
え… rustup っていう Rust インストールを管理する公式ツールがあるのか?
公式なら asdf 使わずに rustup を素直に使う方が良さそう。
そして cargo install と rustup component add は同じようにライブラリをインストールするけど、挙動が違う様子…。
- rustup component add はビルド済みをインストール
- cargo install はソースをダウンロードして手元でビルド
どちらが良いのかはよく分からなかった…。検索をすると cargo でインストールするドキュメントばかりだが。
Rust バージョン管理は asdf よりも公式の rustup の方が良いと言うことなので入れ替える。
asdf plugin remove rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
fish_add_path /Users/zaru/.cargo/bin
これでインストール完了。
rustc --version
rustc 1.70.0 (90c541806 2023-05-31)
cargo コマンドでプロジェクトを作成できるらしい。
cargo new rocket_example --bin
オプションの --bin
は、 src/main.rs
というファイルを自動作成するみたい。他に --lib
というオプションがある。違いは bin は通常の実行バイナリを作る。 lib はライブラリを作る目的のよう。
cargo new すると、ディレクトリとともにファイルが自動作成される。
tree
master
.
├── Cargo.toml
└── src
└── main.rs
メインエディタの IntelliJ で該当ディレクトリを開く。
すると Cargo Check という機能があることに気がついた。なんだこれ。軽く調べてみると、ライブラリやパッケージが壊れていないかを事前にチェックしてくれるものらしい。 cargo build で生成物を作るよりも手前で気がつける。かつキャッシュも聞くので便利…らしい。どれだけ壊れるのか体感がないので便利なのかは今は不明。
IntelliJ の Cargo Check 設定欄を見ると、Clippy というツールも選べる。調べると Clippy はリンターみたい。Cargo Check と比較すると、よりリンターらしいチェックをしてくれると言うことのようだ。どちらかしか有効にできないので Clippy を選ぶことにした。
そのほかにもフォーマッターなどがあるらしい。
とりあえず IntelliJ で rustfmt を有効にしてみた。これがデファクトスタンダードなフォーマットと言うことだろうか。
もろもろいじっていたら Cargo.lock
ファイルが生成されていた。よくあるパッケージのバージョン管理ファイルだろう。git で管理する。ライブラリ目的だと管理しないらしい。
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "rocket_example"
version = "0.1.0"
Rocket を使うために Cargo.toml
を編集する。自分で書いても良いらしいが、さすがに手書きはつらい。普通に cargo add
コマンドで追加できる様子。
cargo add rocket
実行すると Cargo.toml
に以下が追加された。
[dependencies]
rocket = "0.4.11"
チュートリアル記事を見ると、rocket_dyn_templates も使うと良いらしい。これは動的なテンプレートをサポートしてくれるライブラリっぽい。示されている Cargo.toml
を見ると、謎の記法があった。 dependencies
の後にドットでライブラリ名がつながっている。どういうことだろう。
[dependencies.rocket_dyn_templates]
version = "0.1.0-rc.2"
features = ["handlebars","tera"]
普通に cargo add rocket_dyn_templates
すると以下のように普通に書かれる。
[dependencies]
rocket = "0.4.11"
rocket_dyn_templates = "0.1.0-rc.3"
でも rocket_dyn_templates を使った記事を調べると [dependencies.rocket_dyn_templates]
の書かれ方をしている。
リファレンスを確認すると、パッケージの特定機能だけを使いたい場合は、このようにドットを付けて書くらしい。なるほど。
[dependencies.awesome]
version = "1.3.5"
default-features = false # do not include the default features, and optionally
# cherry-pick individual features
features = ["secure-password", "civet"]
Rocket 公式ドキュメントのコードを実行してみる。
#[macro_use]
extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"Hello, world!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index])
}
IntelliJ のエディタでは main()
関数がないよ、と警告されている。とりあえず無視して cargo run
する。
cargo run
# 略 : いろいろパッケージをビルドするログが流れる
error: failed to run custom build command for `pear_codegen v0.1.5`
Caused by:
process didn't exit successfully: `/Users/zaru/Sites/rocket_example/target/debug/build/pear_codegen-738a2be10713487d/build-script-build` (exit status: 101)
--- stderr
Error: Pear requires a 'dev' or 'nightly' version of rustc.
Installed version: 1.70.0 (2023-05-31)
Minimum required: 1.31.0-nightly (2018-10-05)
thread 'main' panicked at 'Aborting compilation due to incompatible compiler.', /Users/zaru/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pear_codegen-0.1.5/build.rs:24:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
エラーになった。Error: Pear requires a 'dev' or 'nightly' version of rustc.
と書かれているので、どうやら dev / nightly な rustc を使ってくれと言うことらしい。安定リリース版じゃないものを使ってくれと言うのは… Rust 的にはよくあることなんだろうか?
ドキュメントを見ると、基本的には安定版を使うっぽい。単に Rocket が依存するパッケージのバージョンが nightly を求めているっぽい。ここら辺のバージョンアップの肌感がよくわからない。 nightly に依存するならそのパッケージも nightly としてリリースされていて欲しいが…。
Nightly な Rust インストールをする。
rustup install nightly
バージョンを確認してみる。
rustup run nightly rustc --version
rustc 1.72.0-nightly (04075b320 2023-06-22)
デフォルトを nightly にする。
rustup default nightly
rustc --version
rustc 1.72.0-nightly (04075b320 2023-06-22)
改めて cargo run
して Rokcet サンプルを動かす。
error: at least one of "tera" or "handlebars" features must be enabled
--> /Users/zaru/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rocket_dyn_templates-0.1.0-rc.3/src/lib.rs:142:1
|
142 | compile_error!("at least one of \"tera\" or \"handlebars\" features must be enabled");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: could not compile `rocket_dyn_templates` (lib) due to previous error
warning: build failed, waiting for other jobs to finish...
次は別のエラーが出た。 rocket_dyn_templates
の機能を有効にしろとのこと。もしかして前に書いた Cargo.toml
で明示的に機能を書く必要があるのか? よく分からないが、とりあえず以下のように修正をした。
[dependencies]
rocket = "0.4.11"
[dependencies.rocket_dyn_templates]
version = "0.1.0-rc.3"
features = ["handlebars", "tera"]
さらに改めて cargo run
して Rocket サンプルを動かす。
error: cannot find attribute `launch` in this scope
--> src/main.rs:9:3
|
9 | #[launch]
| ^^^^^^
error[E0425]: cannot find function `build` in crate `rocket`
--> src/main.rs:11:13
|
11 | rocket::build().mount("/", routes![index])
| ^^^^^ not found in `rocket`
error[E0658]: `macro` is experimental
--> src/main.rs:4:1
|
4 | #[get("/")]
| ^^^^^^^^^^^
|
= note: see issue #39412 <https://github.com/rust-lang/rust/issues/39412> for more information
= help: add `#![feature(decl_macro)]` to the crate attributes to enable
= note: this error originates in the attribute macro `get` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0601]: `main` function not found in crate `rocket_example`
--> src/main.rs:12:2
|
12 | }
| ^ consider adding a `main` function to `src/main.rs`
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> src/main.rs:10:16
|
10 | fn rocket() -> _ {
| ^ not allowed in type signatures
Some errors have detailed explanations: E0121, E0425, E0601, E0658.
For more information about an error, try `rustc --explain E0121`.
error: could not compile `rocket_example` (bin "rocket_example") due to 5 previous errors
別のエラーが出た。エラーが変わるというのは進歩している証拠だ。
Rocket のドキュメントをよく見たら使っている Rocket のバージョンが全然異なる。今、自分が使っているのが rocket = "0.4.11"
だが、ドキュメントには rocket = "=0.5.0-rc.3"
。RC バージョンを積極的にドキュメントで案内している…。やはり Rust はこういう攻めな姿勢でも安定しているよ的な文化なのか?
気を取り直して Rocket のバージョンを入れ替える。再度 cargo run
すると無事 Web サーバが起動できた。
🔧 Configured for debug.
>> address: 127.0.0.1
>> port: 8000
>> workers: 10
>> max blocking threads: 512
>> ident: Rocket
>> IP header: X-Real-IP
>> limits: bytes = 8KiB, data-form = 2MiB, file = 1MiB, form = 32KiB, json = 1MiB, msgpack = 1MiB, string = 8KiB
>> temp dir: /var/folders/j1/11sx50cj3ws85n8tp99thx8r0000gn/T/
>> http/2: true
>> keep-alive: 5s
>> tls: disabled
>> shutdown: ctrlc = true, force = true, signals = [SIGTERM], grace = 2s, mercy = 3s
>> log level: normal
>> cli colors: true
📬 Routes:
>> (index) GET /
📡 Fairings:
>> Shield (liftoff, response, singleton)
🛡️ Shield:
>> X-Content-Type-Options: nosniff
>> Permissions-Policy: interest-cohort=()
>> X-Frame-Options: SAMEORIGIN
🚀 Rocket has launched from http://127.0.0.1:8000
サンプルコードの先頭に書かれている以下のコードは Rocket を使うよと言う宣言。 extern crate
で外部パッケージを読み込むことができる。さらに #[macro_use]
と宣言することでマクロが使える。
#[macro_use]
extern crate rocket;
しかし、調べてみると 2018 以降は use
で書くことができる。マクロを使わないなら、そもそも extern crate
宣言自体が不要で何もしなくても良いらしい。
use rocket::get;
use rocket::launch;
use rocket::routes;
どちらが良いのかは不明。使いたいマクロが大量にある場合は、都度 use
で書くよりも、素直に extern crate
する方が楽っぽい。
公式ドキュメントでも言及されていた。
Note: We prefer #[macro_use], but you may prefer explicit imports.
Throughout this guide and the majority of Rocket's documentation, we import rocket explicitly with #[macro_use] even though the Rust 2018 edition makes explicitly importing crates optional. However, explicitly importing with #[macro_use] imports macros globally, allowing you to use Rocket's macros anywhere in your application without importing them explicitly.
You may instead prefer to import macros explicitly or refer to them with absolute paths: use rocket::get; or #[rocket::get].
Rocket のサンプルアプリを動かしただけではライブリロードは動かない様子。そもそもサポートされているのだろうか?
調べてみると cargo-make
や cargo-watch
を使うと都度ビルドし直してくれるっぽい。
Rocket で Template を使ってみる。 rocket_dyn_template
で外部ファイル View が使える。
use rocket_dyn_templates::{context, Template};
#[get("/")]
fn sample() -> Template {
Template::render(
"sample",
context! {
foo: 123,
},
)
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/sample", routes![sample])
.attach(Template::fairing())
}
context
オブジェクトでテンプレートに渡すパラメータを設定できる。今回のように使い捨ての書き方もできるし、ちゃんと型を指定して渡すこともできる。
View ファイルは xxx.html.tera
と命名し、ルート /templates
ディレクトリに保存する。
<!DOCTYPE html>
<html>
<head>
<title>Templating Example</title>
</head>
<body>
Argument :{{ foo }}
</body>
</html>
変数展開は {{ }}
で行う。
なお、View ファイルに関しては Live Reloading がサポートされているため、View ファイル修正後、ブラウザを再読み込みすると反映されている。これは嬉しい。
tera
というのは rocket_dyn_templates
自身の機能ではなく、 tera
というパッケージの様子。これがテンプレートエンジン。tera
以外にも handlebars
というテンプレートエンジンもサポートされている。
どちらもよくあるテンプレート記法。どちらが良いかは使い込んでみないと分からなそう。とりあえず tera
にしたいが… IntelliJ には tera
のプラグインがない…。handlebars
はあるっぽい。
VS Code には tera
の拡張機能があるな…。
Crate のサイトでダウンロード人気ランキングを見てみる。
1位が tinytemplate
で、2位が handlebars
、3位が tera
だった。
エディタサポートがされていないと厳しいので、ひとまず tera
ではなく handlebars
にすることとした。
tera
から handlebars
への切り替えはファイル名を変更するだけで済んだ。
sample.html.tera
-> sample.html.hbs
試しに POST するサンプルを作ってみる。
use rocket::form::Form;
#[derive(FromForm)]
struct SampleForm {
name: String,
}
#[post("/", data = "<sample_form>")]
fn post_sample(sample_form: Form<SampleForm>) -> Template {
Template::render(
"sample",
context! {
foo: 123,
name: sample_form.name.to_string(),
},
)
}
受け取るパラメータを Struct 構造体で定義して、それを引数として受け取る。
<form action="/sample" method="post">
<input type="text" name="name">
<button type="submit">送信</button>
</form>
<p>入力したのは {{ name }} です</p>
Rocket のルーティングは以下のようにまとめられる。
rocket::build()
.mount("/", routes![foo])
.mount("/", routes![bar])
rocket::build()
.mount("/", routes![foo, bar])
IntelliJ で main function not found
という警告が出る。警告を出さないようにする改善要望 Issue が立ち上がっている。
nightly モードで実験的機能を On にすれば警告が出なくなるらしいが、うまく反映されなかった。
Rocket で POST 受け取るパラメータは struct
で定義できる。
#[derive(FromForm)]
struct SampleForm {
name: String,
}
#[post("/", data = "<sample_form>")]
fn post_sample(sample_form: Form<SampleForm>) -> Template {
}
derive
マクロで FromForm
trait を利用している。Rust の trait は、共通の振る舞いを定義して利用することができる機能。他言語の interface に似ている。trait の機能性はわかったが、derive
マクロと一緒に使うことでどういう機能が提供されているのかは今は不明。
Rocket のコードを見ると以下のコメントが記載されていた。
Each field type is required to implement FromForm.
derive
マクロを使うことで、 struct
で定義している各フィールドは FromForm
trait を実装する必要が出てくる。つまり、自動で trait を適用してくれるマクロ。FromFrom
trait のコードを読むと、初期値や値の変更などのメソッドシグネチャが定義されていた。中身はまだよく分からない。
Rust で Web アプリを作る際の View 変数、型どうなってるの問題。
Rocket + handlebars で構築しているが xxx.html.hbs
ファイルで存在していない変数を参照しても、当然エラーにはならない。しかし開発体験としては View ファイルをエディタで編集する際にひんてぃんぐして欲しいし、エラーになって欲しい。
例えば Next.js のような TypeScript で全体をフロント・サーバサイドを作っている場合は検知可能。
Rust の場合はどのようにすると良いのだろうか?
Yew ライブラリを使うことで Next.js のような(あるいは React / JSX か)体験ができる?
playground で簡単な例を試してみたが、Rust ファイルの中で HTML を JSX のように書けるため、当然のことながら View の変数問題が解決される。存在しない変数は利用できないし、型もちゃんと明確になる。
悪くない選択肢のように感じるが… Yew のコンセプトをまだ理解していない。
Yew を試すためにドキュメントを参照しながらサンプルアプリを作ってみる。
まず cargo-generate
をインストールする。GitHub のリポジトリからプロジェクトを作成することができるツールの様子。
cargo install cargo-generate
何回か cargo でインストールして思ったけど…依存 crate をぜんぶビルドするから時間がかかるな…。
cargo-generate
を入れたら Yew のテンプレートを使ってプロジェクトを作成する。
cargo generate --git https://github.com/yewstack/yew-trunk-minimal-template
実行するとプロジェクト名を聞かれ、入力したらディレクトリとファイルが作成される。
cargo run
実行したら WASM がビルドできないよとエラーが出た。
thread 'main' panicked at 'cannot call wasm-bindgen imported functions on non-wasm targets', /Users/zaru/.cargo/registry/src/index.crates.io-6f17d22bba15001f/js-sys-0.3.60/src/lib.rs:5520:9
プロジェクトの README にびるどターゲットに WASM を追加しろと書いてあったので以下を実行する。
rustup target add wasm32-unknown-unknown
しかし結果は変わらず。そういうことではなかった。 wasm-bindgen
がないというエラーだったか。
cargo install trunk wasm-bindgen-cli
と思ったけど、そもそも cargo run
じゃなかった。ドキュメントでは cargo run
だけど、README は trunk serve
だった。
trunk serve
サーバは起動しそうだったけど、今度は違うエラーが出た。
error
error from HTML pipeline
Caused by:
0: error from asset pipeline
1: failed downloading release archive
2: error downloading archive file: 404
https://github.com/rustwasm/wasm-bindgen/releases/download/0.2.83/wasm-bindgen-0.2.83-aarch64-apple-darwin.tar.gz
うーむ…存在しない。テンプレートプロジェクトが参照している wasm-bindgen は 0.2.83 だが、このバージョンはまだ AppleSillicon 用のバイナリは存在していなかった様子。
cargo update
プロジェクトの crate をアップデートして wasm-bindgen
を 0.2.87 にすると trunk serve
で正常に起動ができた。
Trunk は Rust Web アプリを WASM で動かすためのツールの様子。
Yew で JSX 風に HTML を書けるけど、IntelliJ も VS Code もまともにサポートしているわけじゃないから…書き味はぜんぜん良くない。GitHub Copilot を使っていれば推測して、ある程度は補完はしてくれるんだけど、そういうことじゃない。
Rocket で JSON をレスポンスするコードを書く。
JSON を扱うために以下の機能を読み込む。
use rocket::serde::json::Json;
use rocket::serde::Serialize;
// 以下のようにも書ける
use rocket::serde::{Serialize, json::Json};
すると、以下のようなエラーが出た。
unresolved import `rocket::serde::json` [E0432] could not find `json` in `serde`
調べると Cargo.toml で features = ["json"]
を指定する必要がある様子。
[dependencies]
rocket = "=0.5.0-rc.3"
# 以下に書き換え
[dependencies.rocket]
version = "0.5.0-rc.3"
features = ["json"]
どうやら Crate ではデフォルトですべての機能を有効にするのではなく、オプショナルな機能は features で明示的に有効にする必要があるらしい。おそらく必要のないコードをコンパイルしないようにするため。
Rocket の JSON に関してはコメントで記載があった。
JSON を返す実装例。
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
struct SampleJson {
foo: String,
name: String,
}
#[get("/sample_json")]
fn sample_json() -> Json<SampleJson> {
Json(SampleJson {
foo: "Hello, world!".to_string(),
name: "Taro".to_string(),
})
}
serde
とは Rust のシリアライズをサポートする Crate 。要は Struct データを JSON に変換することができる。serde(crate = "rocket::serde")
として Rocket の serde を使うようにしている?
エディタ上では Unresolved reference:
Json
という警告が出ているが、特に問題なくビルドできる。なんだろうこれ。features トグルで有効にした JSON 機能がエディタで正しく認識されていない可能性が高い。
うーん…
Rocket + sqlx で MySQL なデータを表示するアプリを作ろうとしたけど挫折
sqlx::query! が動かせない…
最初から理解していない複数の Crate を使いこなそうとしたのが間違いだった。まずは sqlx 自体を使ってみることとする。
cargo new sqlx-sample --bin
公式の導入方法を確認すると、tokio
と async-std
の違い、さらに TLS かどうかなどいくつかの方式が存在する様子。
# Cargo.toml
[dependencies]
# PICK ONE OF THE FOLLOWING:
# tokio (no TLS)
sqlx = { version = "0.7", features = [ "runtime-tokio" ] }
# tokio + native-tls
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-native" ] }
# tokio + rustls
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-rustls" ] }
# async-std (no TLS)
sqlx = { version = "0.7", features = [ "runtime-async-std" ] }
# async-std + native-tls
sqlx = { version = "0.7", features = [ "runtime-async-std", "tls-native" ] }
# async-std + rustls
sqlx = { version = "0.7", features = [ "runtime-async-std", "tls-rustls" ] }
大きく2つ分かれている。
- 非同期ライブラリ : async-std, tokio, and actix
- TLS ライブラリ : native-tls and rustls
- rustls 一瞬 result かと思って意味不明だった
これらライブラリの組み合わせを自由にやっていいらしい。
とりあえず検証なので tokio のみでやってみる。
[package]
name = "sqlx-sample"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sqlx = { version = "0.7", features = [ "runtime-tokio", "mysql" ] }
tokio = { version = "1.29.1", features = ["full"] }
cargo run
sqlx インストール自体は無事完了。
- 正式な表記は SQLx だった
- SQLx は ORM ではないと README に書かれている
- ORM を使いたければ ormx や SeaORM を使ってくれ
ドキュメントにあるサンプルコードを元に1件取得して表示させてみる。
use sqlx::mysql::MySqlPoolOptions;
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPoolOptions::new()
.max_connections(5)
.connect("mysql://root:password@127.0.0.1/employees").await?;
// Make a simple query to return the given parameter (use a question mark `?` instead of `$1` for MySQL)
let row: (i64,) = sqlx::query_as("SELECT * FROM employees")
.fetch_one(&pool).await?;
println!("{}", row.0);
Ok(())
}
他の列も取得しようとしたところ、let row: (i64,) = sqlx::query_as
の受け取る部分で型を指定する必要があった。サンプルのテーブルが以下の構造。
desc employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
date
と varchar
の2つを受け取ってみる。date
は chrono という Crate を使うらしい。Cargo.toml に追加する。
[dependencies]
sqlx = { version = "0.7", features = [ "runtime-tokio", "mysql", "chrono" ] }
tokio = { version = "1.29.1", features = ["full"] }
chrono = { version = "0.4.26" }
あとは型を指定して表示させる。
let row: (i64,chrono::NaiveDate,String,) = sqlx::query_as("SELECT * FROM employees")
.fetch_one(&pool).await?;
println!("{}, {}, {}", row.0, row.1, row.2);
Struct で定義して渡すこともできる。基本はこちらの使い方か。
use sqlx::mysql::MySqlPoolOptions;
#[derive(sqlx::FromRow)]
struct Employee { emp_no: i64, birth_date: chrono::NaiveDate, first_name: String, last_name: String }
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPoolOptions::new()
.max_connections(5)
.connect("mysql://root:password@127.0.0.1/employees").await?;
// Make a simple query to return the given parameter (use a question mark `?` instead of `$1` for MySQL)
let row: Employee = sqlx::query_as("SELECT * FROM employees")
.fetch_one(&pool).await?;
println!("{}, {}, {}", row.emp_no, row.birth_date, row.first_name);
Ok(())
}