💭

気楽に Rust で遊びたい Script 感覚で始める Rust

に公開

はじめに

僕は Rust がとてもすきです。
でも型の制約はキツイし、なんだか覚えることがたくさんあるぞ……

TypeScript でよく適当なツールを書いてるのですが、そのくらいの気軽さで Rust で遊べないかな?と思い試してみました。

TypeScript でいう以下の機能が使えたらよさそうですね!

  • fetch
  • JSON.stringify
  • JSON.parse
  • console.log
  • fs.writeFile
  • fs.readFile

Rust の install

まずは Rust と cargo を install します。
すでに install されている場合はスキップしてください。
公式 にある通り shell でこれを実行してみましょう。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Rust の環境構築

続いて作業環境を作ります。
npm init みたいなものですね。

mkdir project
cd project
cargo init

nodemon のようにファイルの変更があれば再起動するツールもほしいですね。
グローバルに install しておきましょう。

cargo install bacon

準備が整いました。

console.log 的なことを試す

すでに src/main.rs が作られているので触ってみましょう。

fn main() {
    println!("Hello, world!");
}

cargo run で実行できます。
bacon を global に install していれば bacon run でファイルの変更があれば再起動できます。

ターミナルに Hello, world! が表示されましたか?
続いて world を work に変更し、保存して再実行してみましょう。
僕らが進むべき道がターミナルに表示されます。

Github api を叩いてみよう

module を install

必要なものを install してみましょう。

  • reqwest(blocking)
    • 同期的に http request する
  • serde, serde_json
    • JSON の serialize/deserialize をする

tokio を使えば非同期(async/await)が使えますが、今回は同期で作ってみましょう。

cargo add reqwest --features blocking
cargo add serde
cargo add serde_json

これまでの作業で Cargo.toml が以下のようになっていれば OK です。

[package]
name = "project"
version = "0.1.0"
edition = "2024"

[dependencies]
reqwest = { version = "0.12.23", features = ["blocking"] }
serde = "1.0.219"
serde_json = "1.0.143"

src/types.rs を作る

別ファイルにあるものを import したくなることがよくあるので import/export 方法を押さえておきましょう。

src/types.rs を作り次のコードを記述しましょう。

use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Config {
    pub url: String,
    pub user_agent: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct GitHubUser {
    pub login: String,
    pub id: u64,
    pub public_repos: u32,
}

pub をつけておくと別のファイルからも参照できるようになります。
use でライブラリを import することができます。

先程も軽く触れましたが serde は JSON の serialize/deserialize をするライブラリです。

#[derive(Debug, Deserialize, Serialize)] をつけておくといい感じにマッピングしてくれます。
#[serde(rename_all = "camelCase")] があると JSON 内ある camelCase を snake_case に変換してくれます。

config.json を作成

url と user_agent を設定する config を project 直下に作りましょう。
以下の内容を config.json というファイル名で保存してください。

{
  "url": "https://api.github.com/users/rust-lang",
  "userAgent": "project/0.1.0"
}

src/main.rs を上書きする

いよいよ実行するコードを書いていきます!
src/main.rs を次のコードに上書きしてみてください。

mod types;

use reqwest::blocking::Client;
use std::fs::{read_to_string, write};
use types::{Config, GitHubUser};

fn main() {
    let config_str = read_to_string("config.json").unwrap();
    let config: Config = serde_json::from_str(&config_str).unwrap();

    let client = Client::builder()
        .user_agent(&config.user_agent)
        .build()
        .unwrap();

    let response = client.get(&config.url).send().unwrap().text().unwrap();

    let user: GitHubUser = serde_json::from_str(&response).unwrap();

    println!("GitHub User Info:");
    println!("- Login: {}", user.login);
    println!("- ID: {}", user.id);
    println!("- Public Repos: {}", user.public_repos);

    let output = serde_json::to_string_pretty(&user).unwrap();
    write("output.json", output).unwrap();
}

流れは次のようになっています。

  1. project にある config.json を読み込む
  2. config に設定してある user_agent と url を使って github の API を叩く
  3. response を JSON に deserialize する
  4. deserialize した JSON を出力する
  5. deserialize した JSON を output.json に書き出す

実行

実行してみましょう。
cargo runcargo r でも実行できます。
ターミナルに次のように表示されたはずです。

> cargo r
    Blocking waiting for file lock on build directory
   Compiling project v0.1.0 (/Users/h/h/rust-scriptsy)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.77s
     Running `target/debug/project`
GitHub User Info:
- Login: rust-lang
- ID: 5430905
- Public Repos: 231

project 直下に output.json が出力されているのでその内容も見てみましょう。

{
  "login": "rust-lang",
  "id": 5430905,
  "public_repos": 231
}

まとめ

今回は気楽に動かしたいので unwrap を使いまくってます。
本番環境でやったら恨まれるのでやめましょう。
ここまでできたらいろんなアイディアが試せそうですね!
Rust を楽しんでいきましょう〜〜〜

ドクターメイト

Discussion