🦀

Pythonista の Rust 事始め - Rich Jones 氏の loop コマンドを読みながら -

2022/06/06に公開約5,500字

はじめに

筆者は日々の仕事で Python を使用しているエンジニアです。
本稿では Pythonista である筆者が Rust を学び始めた経緯を述べると共に、Rich Jones 氏による loop コマンドを通じて簡単な Rust のコードの紹介を行いたいと思います。

Rust とは?

Rust は2016〜2020年に渡り Stack Overflow Developer Survey にて最も人気があるプログラミング言語です。また日本経済新聞の調査によると、日本でも Rust は高収入エンジニアに注目されていると言われています。
日本経済新聞2021年8月2日付け記事
日本経済新聞2021年8月2日付け記事より

Rust はマルチパラダイムなプログラミング言語であり、性能、メモリ安全性、特に安全な並行性を目指して設計されています。
Rust は以下のような特徴を持つプログラミング言語です。

  • 静的型付け言語である
  • ガベージコレクターが存在しない。RAII(Resource Acquisition Is Initialization) に基づくメモリ管理(「所有権」と「借用」の概念)
  • Class は存在せず、その代わりに Structs、Enums、Traits が存在する
  • ジェネリックなデータ型を持つ

特に「所有権」「借用」そして変数の「ライフタイム」は Rust を特徴づける新しい概念です。本稿ではこれ以上の詳しい説明は行いませんので、興味のある方は The Rust Programming Language などをご覧ください。Pythonista である筆者には Rust と Python の比較をしている py2rs も参考になりました。

Rich Jones 氏の思い出

話が急に変わりますが、ここで Rich Jones 氏を紹介させてください。

PyCon Singapore 2019

筆者が Rich 氏のことを知ったのは、2019年にシンガポールで開催された PyCon Singapore 2019 においてでした。Rich 氏は同カンファレンスのキーノートスピーカーであり、 Zappa という Python 製サーバーレスフレームワークの作者です。

https://github.com/zappa/Zappa
Rich 氏のトークはエネルギー溢れる早口な英語でありながら、アメリカ人らしい軽妙な語り口とユーモアのあるシンプルなスライドで分かりやすく、聴衆を魅了するものでした。また、OSSコミュニティのメンバーとして、コミュニティと対立する Amaz○n や Jeffrey Bez○s 氏をジョークを交えながらディスる氏の姿が印象的でした。

PyCon Singapore 2019 については Gihyo.jp に素晴らしいレポートが寄稿されていますので、興味がある方は是非以下をご覧ください。

https://gihyo.jp/news/report/2019/10/2901

PyCon JP 2020

その後 Rich 氏は2020年に PyCon JP 2020 にキーノートスピーカーとして招待されますが、新型コロナウイルス感染症の影響でオンラインでの開催となり、Rich 氏もリモートでの参加となりました。コロナ禍という極めて困難な状況にありながら、いやむしろ困難な状況にあったからこそ、Rich 氏の講演は参加したエンジニアにとても勇気を与えるものでした。そしてやはり、Pycon JP でも Amaz○n や Jeffrey Bez○s 氏をディスる氏の御家芸を見ることが出来ました。どんな状況にあってもテクノロジーへの情熱、そしてジョークとユーモアを忘れない氏の姿に当時の筆者は大いに励まされました。
Rich 氏の当時の講演は YouTube で見ることができるため、興味がある方は是非ご覧ください。

https://www.youtube.com/watch?v=dNWRm21cVBI

このような経緯を経て、Rich Jones 氏は筆者が憧れる Pythonista の一人となりました。

loop コマンド

筆者が Rust に興味を持つきっかけとなった一つが、そんな憧れている Rich 氏が Rust でコードを書いていたことです。
Rich 氏は Rust で loop と呼ばれるコマンドを作成しています。

https://github.com/Miserlou/Loop

loop コマンドについて氏は以下のように述べています。

"UNIX's missing loop command!"

loop コマンドの実行例は以下です。

# Run on controllable timers
$ loop --every 10s -- ls

# Have custom counters
$ loop --count-by 5 -- 'touch $COUNT.txt'

# Loop until a certain time
$ loop --for-duration 8h -- ./poke_server

# Loop until a program succeeds (or fails)
$ loop --until-success -- ./poke_server

loop コマンドは400行にも満たない短いコードで書かれているため、Rust のコードリーディングを始めるのに良い入り口になると考えています。
ここでは loop コマンドをさらにシンプルにしたコードを掲載します。簡単のため --every オプションのみサポートします。

Cargo.toml
[package]
name = "loop"
version = "0.1.0"
edition = "2021"

[dependencies]
structopt = "0.3.26"
subprocess = "0.2.9"
src/main.rs
use std::thread;
use std::time::Duration;
use structopt::StructOpt;
use subprocess::Exec;

fn main() {
    let opt = Opt::from_args();

    let joined_input = &opt.input.join(" ");
    if joined_input == "" {
        println!("No command supplied, exiting.");
        return;
    }

    let time = Duration::from_secs_f64(opt.every);
    loop {
        let result = Exec::shell(joined_input).capture().unwrap().stdout_str();

        println!("{}", &result);

        thread::sleep(time);
    }
}

#[derive(Debug, StructOpt)]
#[structopt(name = "loop", about = "Simple `loop` command")]
struct Opt {
    /// How often to iterate (secs). Default: 1sec
    #[structopt(short = "e", long = "every", default_value = "1")]
    every: f64,

    /// The command to be looped
    #[structopt(multiple = true)]
    input: Vec<String>,
}

// ❯ cargo run -- --every 1 -- date
//     Finished dev [unoptimized + debuginfo] target(s) in 0.04s
//      Running `target/debug/loop --every 1 -- date`
// Mon Jun  6 06:12:20 JST 2022
//
// Mon Jun  6 06:12:21 JST 2022
//
// Mon Jun  6 06:12:22 JST 2022

なお、上記を Python で書くと以下のようになります。

loop.py
import argparse
import subprocess
from time import sleep


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-e", "--every", default=1, help="How often to iterate (secs). Default: 1sec"
    )
    parser.add_argument("input", nargs=argparse.REMAINDER)

    args = parser.parse_args()

    input = args.input

    if len(input) > 0 and input[0] == "--":
        input = input[1:]

    if len(input) == 0:
        print("No command supplied, exiting.")
        return

    while True:
        result = subprocess.run(
            input, check=True, capture_output=True, encoding="UTF-8"
        )
        print(result.stdout)

        sleep(float(args.every))


if __name__ == "__main__":
    main()

# ❯ python loop.py --every 1 -- date
# Mon Jun  6 06:21:27 JST 2022
#
# Mon Jun  6 06:21:28 JST 2022
#
# Mon Jun  6 06:21:29 JST 2022

おわりに

よく知られている通り、新しいプログラミング言語を学ぶには、言語仕様を理解すると共に、その言語で書かれたコードを読んだり、その言語でコードを書いたりして慣れることが重要だと言われています。Rich 氏の loop コマンドは Rust を学び始めるのに良い教材になると考え、筆者の氏に対する個人的な思い出と共に、本稿で紹介をさせて頂きました。

PS: 本稿を書いている2022年6月現在、ようやく世界が新型コロナウイルス感染症の脅威から立ち直り始めているのを感じています。いくつかの主要な海外のカンファレンスはすでに現地で開催され、また今後もオンサイトでの開催が予定されています。さらに、時流に敏感なエンジニアたちは早くも海外のカンファレンスへの参加を再開し始めています。
筆者も再び海外のカンファレンスに参加することを楽しみにしており、Rich 氏のようなすばらしいエンジニアとの新たな出会いを期待しています。

Discussion

ログインするとコメントできます