RUSTでreqwestを触ったら微ハマりしたけど、エラーメッセージに惚れた

# せっかくなので、社内ツールのためにRUST使ってみた

まあ、新しい言語を使う時には定番ですが、社内ツール（自分用）のために使うと「ギリ業務使用経験」＋「まあまあ危険も冒さずに済む」ということで、RUSTを社内ツールで使ってみています。まあ、今更エンジニアに戻ろうと思っているわけでもないのですが、エンジニアをmanageすることはあるでしょうし、そんな時、**手に技があるかどうかで説得力と判断力が変わります**。オードリーたんにはなれないけど、爪のあかくらいは見習いたいよね。

でもってRUSTです。Pythonで書けば一瞬なんだけど、Pythonだと先々Python環境がない人のマシンで動かせないし、pyinstallerは悪い思い出もあるので、別の選択肢を持っておきたいですよね。っていうか、Windowsで実行できるものを作るとき、がっつり度合いを不等号で表すと、私の場合、 bat < PowerShell < VBA < Python with pyinstaller <<< VC++ って感じになるのですが、VC++だと、私、ADHDでミスが多いので、**デバッグに無限の時間がかかる**わけですよ。そんな時、その「<<<」を埋めつつVC++を代替できるかな、という期待があるわけです。

そんなわけで、とりあえず某ツールのAPIをたたくことにしたのですが、こんなの書きました。

```rust
use reqwest::Response;
use reqwest::header;
use serde_json::Value;

const DOMAIN: &str = "https://api.example.com";
const PASSCODE: &str = "foo123bar456baz789";

async fn query() -> Result<(), reqwest::Error> {
    let mut headers = header::HeaderMap::new();
    let mut strpass = String::new();
    strpass.push_str(&"Bearer ");
    strpass.push_str(&PASSCODE);
    headers.insert("AUTHORIZATION", header::HeaderValue::from_str(&strpass).unwrap());

    let mut domain = String::new();
    domain.push_str(&DOMAIN);
    domain.push_str(&"/api/v1/hogehoge?page=1");
    let client = reqwest::Client::builder()
        .default_headers(headers)
        .build()?;

    let res: Response = client
        .get(domain)
        .send().await?;

    dbg!(&res.text().await?); // どんなレスポンスが返ってきたかな？

    Ok(())
}


#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    query().await.unwrap_or(());

    Ok(())
}
```

やったぜ、ぜんぜん非同期な使い方してないけどtokioも使ってみたぜ、よゆー！とかぶっこいていたわけです。Hello Worldの次のステップとしてはまあ上々です。

なお、内容の詳しい説明はここではしません。あとで追記するかもだけど、意味、分かるよね？　**ヘッダに認証情報を仕込んでAPIたたいているだけ**です。

でもって、じゃあそれを意気揚々とparseしてみたら……　query()の中身だけ示しますと……

```rust
async fn query() -> Result<(), reqwest::Error> {
    let mut headers = header::HeaderMap::new();
    let mut strpass = String::new();
    strpass.push_str(&"Bearer ");
    strpass.push_str(&PASSCODE);
    headers.insert("AUTHORIZATION", header::HeaderValue::from_str(&strpass).unwrap());

    let mut domain = String::new();
    domain.push_str(&DOMAIN);
    domain.push_str(&"/api/v1/hogehoge?page=1");
    let client = reqwest::Client::builder()
        .default_headers(headers)
        .build()?;

    let res: Response = client
        .get(domain)
        .send().await?;

    dbg!(&res.text().await?);

    let resp: Value = res
        .json().await?; // これをjsonとしてparseしてみよう！

    Ok(())
}
// 下記のエラーになります
// error[E0382]: use of moved value: `res`
//    --> src\main.rs:30:23
//     |
// 24  |     let res: Response = client
//     |         --- move occurs because `res` has type `reqwest::Response`, which does not implement the `Copy` trait
// ...
// 28  |     dbg!(&res.text().await?);
//     |               ------ `res` moved due to this method call
// 29  |
// 30  |     let resp: Value = res
//     |                       ^^^ value used here after move
//     |
// note: this function takes ownership of the receiver `self`, which moves `res`
//    --> C:\foo\bar\reqwest-0.11.4\src\async_impl\response.rs:146:23
//     |
// 146 |     pub async fn text(self) -> crate::Result<String> {
//     |                       ^^^^
// 
// error: aborting due to previous error; 1 warning emitted
// 
// For more information about this error, try `rustc --explain E0382`.
// error: could not compile `import_test`
```

どぼん。コンパイルエラー食らいました。res.text()は、resをもってっちゃうみたいです。よく考えたら、これってストリームなんですよね。**text()メソッドは、強制的に最後までストリームを消費させて状態を変えてしまうので、resをもってっちゃう**のです。

しかし、RUSTの丁寧なエラーメッセージ、いいですね。初心者にやさしいです。たぶん、初心者じゃなくなったらあんまり見なくなる前提で、初心者向けにしてくれているんじゃない？　知らんけど。

```rust
use reqwest::Response;
use reqwest::header;
use serde_json::Value;


const DOMAIN: &str = "https://api.example.com";
const PASSCODE: &str = "foo123bar456baz789";

async fn query() -> Result<(), reqwest::Error> {
    let mut headers = header::HeaderMap::new();
    let mut strpass = String::new();
    strpass.push_str(&"Bearer ");
    strpass.push_str(&PASSCODE);
    headers.insert("AUTHORIZATION", header::HeaderValue::from_str(&strpass).unwrap());

    let mut domain = String::new();
    domain.push_str(&DOMAIN);
    domain.push_str(&"/api/v1/hogehoge?page=1");
    let client = reqwest::Client::builder()
        .default_headers(headers)
        .build()?;

    let resp: Value = client
        .get(domain)
        .send().await?
        .json().await?;

    dbg!(&resp);

    let strparam = resp
        .get(0).unwrap()
        .get("hogehoge").unwrap();

    dbg!(&strparam);

    Ok(())
}


#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
    query().await.unwrap_or(());

    Ok(())
}
```

そんなわけで、そのままの解決ではなく、json化したやつをdbg!で出力すればレスポンスの全文が確認できることが分かったので、解決です。

とはいえreqwestは本当に必要になるまで置いておくことにして、**当面ureq使います**。

# 追記

RUST、いいですね。タイプ量は多いけど、Javaや古いコンパイル言語ほどではないし、所有権の考え方も、早期に勘違いを正してくれるので好きです。**こまめに物差しで叩かれながら躾けられている感じ**です。エラーメッセージの日本語化がなされたら、もっと国内で流行るかもしれませんね。