せっかくなので、社内ツールのためにRUST使ってみた
まあ、新しい言語を使う時には定番ですが、社内ツール(自分用)のために使うと「ギリ業務使用経験」+「まあまあ危険も冒さずに済む」ということで、RUSTを社内ツールで使ってみています。まあ、今更エンジニアに戻ろうと思っているわけでもないのですが、エンジニアをmanageすることはあるでしょうし、そんな時、手に技があるかどうかで説得力と判断力が変わります。オードリーたんにはなれないけど、爪のあかくらいは見習いたいよね。
でもってRUSTです。Pythonで書けば一瞬なんだけど、Pythonだと先々Python環境がない人のマシンで動かせないし、pyinstallerは悪い思い出もあるので、別の選択肢を持っておきたいですよね。っていうか、Windowsで実行できるものを作るとき、がっつり度合いを不等号で表すと、私の場合、 bat < PowerShell < VBA < Python with pyinstaller <<< VC++ って感じになるのですが、VC++だと、私、ADHDでミスが多いので、デバッグに無限の時間がかかるわけですよ。そんな時、その「<<<」を埋めつつVC++を代替できるかな、という期待があるわけです。
そんなわけで、とりあえず某ツールのAPIをたたくことにしたのですが、こんなの書きました。
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()の中身だけ示しますと……
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の丁寧なエラーメッセージ、いいですね。初心者にやさしいです。たぶん、初心者じゃなくなったらあんまり見なくなる前提で、初心者向けにしてくれているんじゃない? 知らんけど。
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や古いコンパイル言語ほどではないし、所有権の考え方も、早期に勘違いを正してくれるので好きです。こまめに物差しで叩かれながら躾けられている感じです。エラーメッセージの日本語化がなされたら、もっと国内で流行るかもしれませんね。