Don't Repeat Yourself

Don't Repeat Yourself (DRY) is a principle of software development aimed at reducing repetition of all kinds. -- wikipedia

ScalaMatsuri 2020に行ってきた! #ScalaMatsuri

2020/10/17-2020/10/18で開催されていた ScalaMatsuri に参加してきました。お忘れかもしれませんが本職は Scala エンジニアです。会社のスポンサー枠で参加しました。

聞いたセッション

スライドリンク集はこちらに。ありがとうございます。

自由、平等、ボックス化されたプリミティブ型

日本語のセッションでした。Java の歴史をさらって問題意識などを振り返りつつ、Scala の等価性の問題を知ることができました。これは Java 側の実装との根深い問題で、それをよい方向に進ませるためにこれまでいくつか修正が行われてきたようです。

dotty に、等価性に関連する新しい機能が追加されていてなんでだろうと思っていたんですが、Scala の等価性判定には不健全性が含まれていて、それに対する対応を行うために行われた機能追加だったのだということをしることができました。

eed3si9n.com

Caliban: Functional GraphQL Library for Scala

英語のセッションでした。Scala で GraphQL なライブラリ Caliban の設計思想などが紹介されていました。

ZIO がなんか最近あつい気がします。私も使ってみたい。

www.slideshare.net

Dotty ではじめるマルチステージプログラミング入門 / An introduction to multi-stage programming with Dotty

日本語のセッションでした。Dotty の新機能マルチステージプログラミング(MSP)。Dotty はこのあたりがすごく強化されていて、使うの楽しみです。

Tagless-final のところが非常に勉強になりました。私は F[_] くらいの理解しかしていませんでしたが、もともとは埋め込み DSL を構築するための手法で、研究者さんなどは便利だからこれを使うというような話がおもしろかったです。

github.com

Akka Streams vs Spark Structured Streaming

日本語のセッションでした。ちょうど Akka Streams を導入してログのストリーミングをしようとしていたのですが、Spark Structured Streaming という選択肢もあることを知りました。ただ、Spark の方は実質マイクロバッチで、使う際には注意が必要そうに感じました。

www.slideshare.net

モダンなJVMマルチスレッド / Modern JVM Multithreading

英語のセッションでした。JVM のマルチスレッドプログラミングに関するいい復習になりました。ただ、わたしは Quill は推しません。

pjurczenko.github.io

オンラインカンファレンスとしての工夫

非常に完成度の高いオンラインカンファレンスでした。運営の方にはぜひ知見をご共有いただきたいです…!

Rust.Tokyo も今年11月7日〜8日に RustFest Global を開催予定なので、オンラインカンファレンスでどのような工夫をしているのかを見ていました。

  • セッション自体は Zoom Webinar を使用して開催。
    • 各部屋にモデレータの方がいて、会の進行を管理していました。
    • セッション中のチャットは Discord にて行われていました。
  • Webinar 上にて同時通訳を配信。
    • 英語か日本語か選べて、日本語セッションときは裏で英語に同時通訳していました。Rust.Tokyo も本当はやりたい。
  • Discord をフル活用。
    • いわゆる認証を通すと、隠し Discord チャンネルが表示されるようになる。
    • スポンサーブースも Discord 上に存在。
    • チャンネルは参加者が自由に作っているようだった(会の途中で #functor なるチャンネルが作成されるなど笑)。
  • 懇親会は Discord 上にて実施。
    • 私は参加していませんが、ちらっと覗いた感じトークルームを使用して開催されていたようです。

Discord は普段友人とゲームのボイチャをするために使っていたくらいだったので、こんなに機能があったのかと衝撃を受けました(笑)。

最近の社内の Scala の状況

最後に。

個人ブログに会社のことを書いても仕方ないかもしれませんが、まとめておくと:

  • 完全に Go に押されている: 私のいる部署はもともと Scala が強い部署だったのですが、最近では新しいプロダクトが立ち上がるとなると、Go が採用されることの方が増えてきています。Go は社内での導入事例がたくさんあり、また他社でのはやりもあって多く採用されています。
  • 新人さんは Scala を難しいと感じるらしい: 「Scala がわからない」とよく言われます*1インターンを多く受け入れているのでわかるのですが、Java の経験がまったくない方が多く来ます。他のアルバイト先やインターン先は Rails を使っていますという方が多く、まず Java のインタフェースなどの概念の習得に苦戦している印象があります。次に苦戦しているのは Future か implicit。

です。

Web 系で、なおかつうちのようにプロダクトの興亡が激しい会社の場合、「そのときはやっている技術」を使ってプロダクト作った後、とくに更改することなくプロダクトが畳まれてしまうということがあります*2。この「そのときはやっている技術」に Go が入っているので、結果的に Go の採用事例が増えているようには思います。逆に、Scala 製のプロダクトが畳まれて、結果的に採用数が減っているように見えるといったところでしょうか。

新人さんを見ていての苦戦ポイントは、彼ら彼女らは Java に登場する概念に馴染みがなく、結果的にまず Java のエコシステムの習得に少し苦労しているようだという印象です。キャリアの中で Scala第一言語としてやる際に苦戦しているのはまず traitabstract class の違いや、それ以外のポイントですと Future の理解や implicit の理解もなかなかの苦戦ポイントのようです。

プロダクトでは Scala を中心に実装されているので、引き続き Scala エンジニアの採用や育成などは肝になってくるのですが、一方で社内勢力が減ってきているのも事実という現状です。さて、これからの技術戦略をどうしていこうかなというのが私が抱える課題になっています。なんとか Scala の社内での盛り上がりを作っていきたいです。

*1:わからない、をわかるように教えるのが教える側の責任なので、これは教える側に問題があります

*2:これは経営戦略の違いなので、長期間続くプロダクトを作れることとどちらが優れているという話には帰結しません。

My Rust 2021

It will be 3 years since I started to code in Rust. I had started to code 6 years ago, so half of my career have been dedicated to write code in Rust. Over recent years, I organised a Rust conference happened in Japan, and I'm now organising RustFest Global with amazing organisers (big thank you to Flaki, Jan-Erik, and other warm people. We're now working hard, stay tuned on Nov 7th-8th!).

I decided to jot down about Rust 2021. My expectations for the future of Rust are the following:

  1. Support async function in trait (and impl).
  2. Describe the difference between tokio and async-std more clear.
  3. Work on more materials for intermediate / advanced Rustaceans.

Support async function in trait (and impl)

Refs to Support `async fn` in trait methods (async/await follow-up) · Issue #2739 · rust-lang/rfcs · GitHub.

Now the Rust users try to implement the following code, we should use async-trait crate. For instance, the following one will be compile error.

trait AsyncTrait {
    async fn f() {
        println!("Couldn't compile");
    }
}

Then we will get an error:

async-trait-sandbox is 📦 v0.1.0 via 🦀 v1.44.0 on ☁️  ap-northeast-1
❯ cargo check
    Checking async-trait-sandbox v0.1.0
error[E0706]: functions in traits cannot be declared `async`
  --> src/main.rs:8:5
   |
8  |       async fn f() {
   |       ^----
   |       |
   |  _____`async` because of this
   | |
9  | |         println!("Couldn't compile");
10 | |     }
   | |_____^
   |
   = note: `async` trait functions are not currently supported
   = note: consider using the `async-trait` crate: https://crates.io/crates/async-trait

To avoid this error for a meantime, we should add a crate async-trait. Add the following line to Cargo.toml.

[dependencies]
async-trait = "0.1.36"

Add #[async_trait] attribute then we will be able to pass the strict compiler :)

use async_trait::async_trait;

#[async_trait]
trait AsyncTrait {
    async fn f() {
        println!("Could compile");
    }
}

async-trait is an awesome crate that enable us to async code being more flexible. However, I want Rust to support async fn in trait.

In developing web applications, we're used to build them with Dependency Injection (DI). When we're to use DI, interfaces and implementations are decoupled by using trait. For instance, I'm usually architecting components that fetch data from persistence layers as bellow:

#[async_trait]
pub trait TodoRepository {
    fn get(&self, id: String) -> Result<Option<Todo>, AppError>;
    fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>;
    fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>;
    fn delete(&mut self, id: String) -> Result<(), AppError>;
}

#[async_trait]
impl TodoRepository for TodoRepositoryImpl {
    fn get(&self, id: String) -> Result<Option<Todo>, AppError> {
        // implementation
    }

    fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> {
        // implementation
    }

    fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> {
        // implementation
    }

    fn delete(&mut self, id: String) -> Result<(), AppError> {
        // implementation
    }
}

The #[async_trait] attribute needs to set both on trait and impl but that's a bit noisy. I wanna define async fn directly as below.

pub trait TodoRepository {
    async fn get(&self, id: String) -> Result<Option<Todo>, AppError>;
    async fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>;
    async fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError>;
    async fn delete(&mut self, id: String) -> Result<(), AppError>;
}

impl TodoRepository for TodoRepositoryImpl {
    async fn get(&self, id: String) -> Result<Option<Todo>, AppError> {
        // implementation
    }

    async fn insert(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> {
        // implementation
    }

    async fn update(&mut self, todo: Todo) -> Result<Option<Todo>, AppError> {
        // implementation
    }

    async fn delete(&mut self, id: String) -> Result<(), AppError> {
        // implementation
    }
}

I feel it more natural in case I use async fn than if use the handy crate.

Describe the difference between tokio and async-std more clear

Every time I introduce Rust to my coworkers (they're web developers as well), I always feel tough to describe the difference between tokio and async-std because there seems to be no clear description or documentation regarding the difference of two runtimes. I can find related posts on Reddit, on the other hand can't find official statements.

Work on more materials for intermediate / advanced Rustaceans

Move on to the next topic. The next one is about Rust books and guides.

Now we have a lot of introduction guides thanks to the community and enthusiastic users but mostly for beginners. We have few kinda advanced guide we can find out the same in other languages as "Effective XXX".

As mentioned in the Rust survey 2019 results, those who have finished "The Rust Programming Language" and used Rust for a long time seem to hope to read advanced material.

The topic will be about (these are a homage to "Effective Python" lol):

  • Rustic Thinking (how to design Rust application with trait and struct, tackling on clone(), hacking with smart pointers, and so on)
  • Functions (introduce some useful functions)
  • Testing and debugging (mocking, integration tests and how to use gdb on Rust, and so on)
  • Speeding up our Rust application

That's just a thought but looks good doesn't it?

That's it

That's it. Good luck everyone!

x.py について

x.py とは

Rust コンパイラ向けのツールです。Python が事前にインストールされている必要があります。

Rust コンパイラのビルド

とっても簡単。下記コマンドを実行しましょう。

まずはクローンします。

git clone https://github.com/rust-lang/rust.git

ビルドに必要なツールが README に記載されているので、それらをインストールする必要があります。私は macOS でビルドしたのですが、macOS の場合はだいたいデフォルトで入っているはずです。ただ、makecmakeninja はインストールが必要かなと思います。

https://github.com/rust-lang/rust#building-on-a-unix-like-system

回線の速度にもよりますが、まずまず時間がかかるので、紅茶でも飲みながら待ちます。

cp config.toml.example config.toml
./x.py build && ./x.py install

build を開始するとまずまず時間がかかるので、新しく紅茶を作って待ちます。ちなみに、この build を成功させるためには、10-15 GB 程度の空き領域が必要になります。

ちょっとした小ネタ

ただ、逐一ビルドしていると時間がかかって仕方がないので、そのような場合には ./x.py check が有効です。これは実質 cargo check コマンドのようなもので、ちょっとしたリファクタリングコンパイル結果をチェックするなどといった用途に使用できます。

テストは ./x.py test コマンドで実行できます。

その他使うコマンドとして、./x.py fmt コマンドがあります。これも同様に cargo fmt のように Rust のコードをフォーマットするものです。Rust のプルリクエストのレビューを見ていると、たまにコミット前に fmt を実行し忘れて指摘されているものを見かけます。コミットする前に忘れずに回しておきましょう。

TwitterFuture の Future#collect と並列処理

よく忘れるのでメモします。Scala です。

Future#collect とは

com.twitter.util.Future についている便利関数で、Seq[Future[A]] を受け取り、Future[Seq[A]] を返すことができる。別名 sequence と呼ばれる処理をします。

この関数はリストになった Future を受け取って処理をしていくのですが、途中で1つでも Future が例外状態になった場合は、そこで処理が中断されます。もし、中断せずに処理を続行させたい場合には、Future#collectToTry という関数を利用できます。

この関数のシグネチャ的に、並列処理を裏側でやってくれそうな気がするのですが、通常通り Future を投げ込む限りでは並列処理はやってくれません。いくつか実験して調べてみましょう。

実験内容

下記の実験を今回は行います。

  • タスクを3つ用意する(A, B, Cと名前をつける)
  • 1秒に1回「ticking task N」(N には A, B, C のどれかが入る)と標準出力する。
  • 期待値としては
    • 逐次実行の場合は、A が終了してから B、B が終了してから C と順にタスクが走る。
    • 並列実行の場合は、A, B, C が同時にスタートする。

実験1: 逐次実行することを確かめる

次のようなテストを書くと、その挙動を確かめることができます。テストライブラリには ScalaTest を使用しています。

import com.twitter.util.{Await, Future}
import org.scalatest.FunSpecLike

class ParalleliseTest extends FunSpecLike {

  describe("1: 逐次実行するパターン") {
    it("正しく実行されること") {
      val taskA = Future {
        for (_ <- 0 until 10) {
          println("ticking task A")
          Thread.sleep(1000)
        }
      }

      val taskB = Future {
        for (_ <- 1 until 10) {
          println("ticking task B")
          Thread.sleep(1000)
        }
      }

      val taskC = Future {
        for (_ <- 1 until 10) {
          println("ticking task C")
          Thread.sleep(1000)
        }
      }

      Await.result(Future.collect(Seq(taskA, taskB, taskC)))
    }
  }
}

結果は下記のようになります。IntelliJ の計測だと、逐次実行しているので 29s かかって終了しているようです。

ticking task A
ticking task A
ticking task A
ticking task A
ticking task A
ticking task A
ticking task A
ticking task A
ticking task A
ticking task A
ticking task B
ticking task B
ticking task B
ticking task B
ticking task B
ticking task B
ticking task B
ticking task B
ticking task B
ticking task B
ticking task C
ticking task C
ticking task C
ticking task C
ticking task C
ticking task C
ticking task C
ticking task C
ticking task C

実験2: 並列実行することを確かめる

FuturePool を使用して処理を記述すると並列処理させることができます。これを利用して実装します。val pool = FuturePool.unboundedPool にて、FuturePool を用意しています。

import com.twitter.util.{Await, Future, FuturePool}
import org.scalatest.FunSpecLike

class ParalleliseTest extends FunSpecLike {

  describe("2: 並列処理するパターン") {
    val pool = FuturePool.unboundedPool

    it("正しく実行されること") {
      val taskA = pool {
        for (_ <- 0 until 10) {
          println("ticking task A")
          Thread.sleep(1000)
        }
      }

      val taskB = pool {
        for (_ <- 1 until 10) {
          println("ticking task B")
          Thread.sleep(1000)
        }
      }

      val taskC = pool {
        for (_ <- 1 until 10) {
          println("ticking task C")
          Thread.sleep(1000)
        }
      }

      Await.result(Future.collect(Seq(taskA, taskB, taskC)))
    }
  }
}

結果は下記のようになります。

ticking task A
ticking task B
ticking task C
ticking task A
ticking task C
ticking task B
ticking task A
ticking task B
ticking task C
ticking task A
ticking task C
ticking task B
ticking task A
ticking task C
ticking task B
ticking task A
ticking task B
ticking task C
ticking task A
ticking task B
ticking task C
ticking task A
ticking task B
ticking task C
ticking task A
ticking task C
ticking task B
ticking task A

並列処理(というか、並行処理?)をしていることがわかります。IntelliJ の計測だと 10s 弱かかって終了しているようです。結果の Seq に詰め込まれる要素順はこの場合、必ずしも保証されないようなので注意が必要そうです。

余談: sequence 3兄弟

TwitterFuture には、Seq[Future[A]] (あるいは、Seq[A]A => Future[B] にリフトさせる関数)を受け取り Future[Seq[B]] を返す関数が3つあります。

  • collect
  • collectToTry
  • traverseSequentially

このうち、collect と collectToTry は Seq を受け取った後、中で iterator に変換します。Seq の数が増えてくると遅延評価を利用したくなりおそらく Stream にしたくなると思うのですが、collect に Stream を入れたとしても iterator にて一度展開されるため、遅延評価にならず意味がなくなってしまうように思います。

この点に注意が必要で、私も実際プロダクトで使っていて、メモリに一気に載せているような挙動を示していました。traverseSequentially にしたら、Stream らしい挙動を示しました。

collect は fs.iterator にて確かに Iterator を呼び出しています。

def collect[A](fs: AnySeq[Future[A]]): Future[Seq[A]] =
    if (fs.isEmpty) emptySeq
    else {
      val result = new CollectPromise[A](fs)
      var i = 0
      val it = fs.iterator

      while (it.hasNext && !result.isDefined) {
        it.next().respond(result.collectTo(i))
        i += 1
      }

      result
    }

実装はこちら

  def collectToTry[A](fs: AnySeq[Future[A]]): Future[Seq[Try[A]]] = {
    //unroll cases 0 and 1
    if (fs.isEmpty) Nil
    else {
      val iterator = fs.iterator
      val h = iterator.next()
      if (iterator.hasNext) {
        val buf = Vector.newBuilder[Future[Try[A]]]
        buf.sizeHint(fs.size)
        buf += h.liftToTry
        buf += iterator.next().liftToTry
        while (iterator.hasNext) buf += iterator.next().liftToTry
        Future.collect(buf.result)
      } else {
        Future.collect(List(h.liftToTry))
      }
    }
  }

実装はこちら

一方でどうやら、traverseSequentially の場合はそのような事態は起こらないように見えます。as.foldLeft をすれば、たとえば as が Stream だった場合は、Stream の foldLeft 実装が呼び出されるはずだからです。

def traverseSequentially[A,B](as: Seq[A])(f: A => Future[B]): Future[Seq[B]] =
    as.foldLeft(Future.value(Vector.empty[B])) { (resultsFuture, nextItem) =>
      for {
        results    <- resultsFuture
        nextResult <- f(nextItem)
      } yield results :+ nextResult
    }

実装はこちら

まとめ

  • Future#collect は逐次実行をする。
  • FuturePool と合わせると並列実行になる。

『実践Rustプログラミング入門』を書きました

すごく今更感がありますが、先週末出版しました。

私のプライベートがとても忙しくしばらく書けませんでした。書籍を書きましたのでご報告です。

ちなみに、著者、まだ現物を受け取っていません。書店で現物を触りたいなと思って見に行きましたが、今週末は在庫切れで本屋さんにありませんでした。

電子書籍は調整中です。

私の担当は1章、3章の一部、11章の一部です。

他の共著者のみなさんの記事

(2020/09/22 追記)

共著なのにこの記事のタイトルを「書きました」としてしまったので、私もだぞ、とみなさんがタイトルで煽ってきています(違

どのような本か?

他のプログラミング言語である程度経験を積んだジュニア(業界経験5年未満程度)のソフトウェアエンジニアやプログラマが、次に新しくRustを学ぶ際の道標となることを目指して書きました。今回は、ジュニア向けにわかりやすい内容になっているかを重視したため、フォルシアさんの新卒〜若手のエンジニアの方に監修に入っていただきました。みなさんありがとうございました。

一方で、Rustに関する突っ込んだ説明*1はほぼ省略しているため、プログラミング言語がとても好きな方や、業界歴が20年となるようなシニアの方にはすこし物足りない内容になっているかもしれません。そうした説明は、『プログラミング Rust』や『実践 Rust 入門』が詳しいかなと思っています。

プログラミングRust

プログラミングRust

しかしそういった方でも別の側面で楽しめるように、具体的なアプリケーションの作り方について、かなり多くの解説を加えました。「実践編」以降は各章で1つのアプリケーションを作り上げていく構成にしてあります。とくに国内の本ですと、GUI 、WebAssembly や組み込み開発の解説が入っている本はまだないかと思います。私のような普段 Web しか触らないエンジニアが、組み込み開発もすこしかじってみるなどの用途にも利用できると思います。

余談ですが、書名が組み換えパズルのようになっていて一部で話題になっています。Rust の和書は出版されている限りで、

といった感じで、「Rust」「プログラミング」「入門」「実践」の4つが順番を変えただけのような書名です…*2。もはやネタです笑。

Rust の書名メーカーなんていうものまで出てきました。著者の一人である @qnighy が作ったようです。よかったら遊んでみてください😌

shindanmaker.com

この本の愛称についてですが、表紙に歯車が多く描かれていることから、著者的には「歯車本」なんて呼ばれると嬉しいねという話をしていた(たしか)のですが、SNS を見る限りでは歯車本と自然に呼んでいただけているようです。ありがとうございます!😃💕

著者陣について

フォルシアさんの主催する勉強会で登壇したエンジニアが著者になりました。詳しくは巻末の著者紹介をご覧ください。昨年12月末に各著者に声掛けがあり*3、打ち合わせをしました。

フォルシア社による著者インタビューも公開されています。

www.forcia.com

わたしと Rust

Rust は2017年の初頭くらいから使い始めました。今では 2015 Edition と呼ばれるころの Rust です。

日本の Rust コミュニティへの初参加は、たしか2017年の10月くらいにあった Rust のハンズオンの会が最初だったと思います。その頃は転職が決まっていて時間に余裕がありました。また、後にたまに LT 会などで登壇していました。

LT 会がきっかけで、Rust.Tokyo のオーガナイザーや今回の書籍執筆にお声掛けいただきました。どちらかというとコミュニティを作っている側かもしれません。

書き方、執筆期間について

計画は1月に立てられ、その後 COVID-19 の流行にともなって、著者同士が連絡を取らない期間が4ヶ月ほど続きました。本の企画はよく立ち消えになることもあると聞いていたので、立ち消えになるかなと思いましたが、5月いっぱいくらいで作業をし、6月中旬に脱稿しました。

原稿そのものは GitHub を使用しました。GitHub 上にマークダウン形式のファイルを置いて、そのファイルを書き換えながら原稿を書き上げていきました。

脱稿後は、まず Dropbox で編集者が pdf ファイルを送信し、私たちは Dropbox 上で当初作業していました。しかし Dropbox の閲覧機能があまりに重く、Google Drive にアップロードし直して作業をしました。Google Drive は圧倒的に速かったです。

脱稿→一校→二校→念校→印刷という流れでレビューは行われました。これは他の出版社でも同じでしょうか。各フェーズの間は、だいたい2週間程度です。印刷まではあっという間でした。

本を書くということ

多くの本を書く人がそうなのかもしれませんが、私にとってもやはり、知識を体系的にまとめ直すいい機会だったように思います。たとえば Rust については、これまでの登壇や記事でいくつか書いてきていたため、断片的に知っていることは多々ありました。発表してある程度形になっていたものに加えて、周辺知識を体系的にまとめ直すことができました。その成果は、先日発表したこのスライドに実を結んだように思います。

さらに、体系的な知識のみならず読者層に合わせて文章を構成するという作業が入ります。知っていることを再度まとめ直しつつ、多くの識者が言っていることを参考資料として組み込みつつ、読者が求めている形に構成を組んで文章に落とす必要があります。これが意外に大変な作業でした。

とくに読者のニーズに応えるというのが難しかったです。1章は一度、前の原稿がボツになっています。読者層と合わないと判断したためです。このボツになった原稿は、プログラミング言語をそれなりに触ってきた方(しかも、C/C++ ならびに Haskell などの関数型まで含む様々な言語を)向けに構成されていました。しかし、この本の想定読者は、どちらかというとフロントエンドで JavaScript をメインに触っていましたという方や、PythonJava を普段書いていますという方です。なので、その方たちに興味をもってもらえるように一度書き直しました。ボツになった原稿は近々公開したいと思っています。

結果的に SNS での反応を見る限りでは、1章の構成は違和感なく受け入れられているようで本当によかったです。

私の個人的な話になりますが、本を書くのは小さい頃からの夢でした。大学生の頃はジャーナリストか戦略コンサルタントになりたくて、その分野でいつか本を書く人間になれたらいいなあと思っていたのですが、なんと予想もしなかったソフトウェアエンジニアとして出版することになりました。人生何があるかわかりませんね。

今は個人の時代と言われ、誰でも個人のメディアを持てる時代になってきています。出版も、自費出版や技術書典などのすばらしい企画によって、一部の特権的な人々だけの存在ではなくなってきたように思います。これは本当にすばらしいことです。人間は文字を使って、後世に知識を使える形で残してきたからこそ発展してきました。多くの人が、自身の学んだことをより多くの人に役立ててもらおうと伝えようとすること――これはとても尊い行為だと思います。

しかし、やはり出版社から出版するというのは、それらとは違い「プロのクオリティ」を求められることになります。ステークホルダーの数が自分ひとりのメディアで記事を書くのとは桁違いに多くなります。それだけ情報発信に責任も伴うことになります。その緊張感を味わえたこともまた、大きな人生経験になりました。機会をいただきほんとうにありがとうございました。

次回作

今回気づいたのですが、私は文章を書くのはそこまで苦ではないタイプです。なので、技術書典などに出版してみようかなと思いました。

私は Web を扱う企業で、技術選定の際に Rust が候補のひとつにあがることを夢見ているので、『Rust による Web アプリケーション開発』(元ネタ)という本を次は書こうかなと思っています。async/await に関する話も、そちらでもっと突っ込んで書くことにしようと思います。

*1:たとえば rustc がどのように AST 以降を解釈していくかや、トレイトの理論的な細かい説明など

*2:書名については当初、編集者さんがすでにある程度決めていて、その時点で「Rust プログラミング入門」という名前がついていました。でも、ダブっていると著者が主張して、頭に何かつけようかという話になり、実践的なアプリケーションの開発を重視した本だから頭に「実践」をつけようとなりました。

*3:編集者さんがポロッと言っていたことで印象的なことがあるのですが、業界的に、たとえば Rust なら競合がいるから入門書の出版をやめようか、となる感じになるのではなく、その出版社内でのラインナップの関係で決めるようです。秀和システムさんが Rust の入門書をラインナップにもっておらず、今回の企画が立ち上がったようです。秀和さんだけかもしれませんが。

riscv-gnu-toolchain を macOS にインストールする

RISC-V のエミュレータを実装する際などに使用する riscv-gnu-toolchain を macOS にインストールしてみたので、メモを残しておきます。

github.com

インストール手順

git clone と make linux でかなりの時間を要します。時間に余裕をもって実行することをおすすめします。

1. clone する

ディレクトリはどこでも構わないので、上記の riscv-gnu-toolchain リポジトリを手順通り git clone します。

$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain

手元のネット環境にもよりそうですが、おおよそ1時間くらいかかりました。

2. ビルドの前準備

最終的には make でビルドしますが、その前に必要なツールキットをインストールしておきます。

$ brew install python3 gawk gnu-sed gmp mpfr libmpc isl zlib expat

Python3 以外は各ツールをまったく知らなかったので、ちょっと調べてみました。

  • gawk: awkGNU 実装。GNU + awkgawk かな?
  • gnu-sed: 名前の通りだが、GNUsed
  • gmp: GNU Multiple Precision (Arithmetic Library)←頭文字を取って gmp多倍長整数などを扱うライブラリ。
  • mpfr: 多倍長浮動小数点演算を扱うライブラリ。
  • libmpc: 多倍長整数を扱うライブラリ。
  • isl: Integer Set Library の頭文字を取ったもの。整数の集合に関する演算を扱うライブラリみたい。
  • zlib: データの圧縮を扱うライブラリ。
  • expat: XML パーサー。

3. 参照ディレクトリを設定する

今回は、/opt/riscv というディレクトリに成果物をビルドしてもらうことを想定しています。下記コマンドを叩くと、どこを見るかを指定できます。

$ ./configure --prefix=/opt/riscv

ちなみに、今回私が作っている RISC-V エミュレータでは、rv64g というアーキテクチャに対するクロスコンパイルを行いたいので、configure は下記のように設定します。

$ ./configure --prefix=/opt/riscv --with-arch=rv64g

4. Newlib と Linux のクロスコンパイラをインストールする

手順に従ってインストールしていきます。

$ make & make linux

これもかなり時間を要します。30分〜1時間くらいかかったように思います。

5. bin に PATH を通す

PATH を通しましょう。下記のように export するか、

$ export PATH=$PATH:/opt/riscv/bin

.zshrc.bashrc に下記のように設定します。

PATH=$PATH:/opt/riscv/bin

おまけ: アセンブラをバイナリに変える

私が今回やりたかったのは、下記の RISC-V アセンブラをバイナリに変えることでした。なので、最後にバイナリを作成しましょう。

main:
  addi x29, x0, 5
  addi x30, x0, 37
  add x31, x30, x29

上記を add-addi.s として保存しているものとします。elf-gcc で elf を作り、その後 elf-objcopy で Plain Binary に変換します。

riscv64-unknown-elf-gcc -Wl,-Ttext=0x0 -nostdlib -o add-addi add-addi.s
riscv64-unknown-elf-objcopy -O binary add-addi add-addi.bin

これでエミュレータに通すことができました。

参考資料

Shinjuku.rs#10 に参加した #Shinjukurs

ブログ枠で参加しました。なので、書きます。

forcia.connpass.com

RustでProperty-Based Testing

(自分の職場のデスクで聞いていたので、めっちゃ話しかけられて半分くらいしか聞けませんでした…)

Rust で Property-Based Testing する際にはどういったクレートが使えるか?という話でした。

Property-Based Testing というのは、ざっくりいうとシステムの仕様からテストケースを半自動で作成する手法のことです。

テストと実装は多くの場合密結合になってしまうため、たとえば実装側であるデータにフィールドを追加すると、テスト側も修正し、そのテスト内容やエッジケースの検出も隅々まで修正する必要があります。Property-Based Testing を用いると、フレームワークがテストケースを(マクロやリフレクションなどでデータ構造を読み取って)自動生成するため、テストと実装を疎結合にすることができるというのが、この手法のよいところです。

Property-Based Testing についてのプレゼンテーションで、クロネコヤマトの荷物の配送条件に関するテストを実装していく中で、Property-Based Testing を使ってテストをするという内容でした。Rust では下記の2つが候補として上がるようです。

コード例はまた後日考えて書いて載せてみます。

Crate Trendsを作ってみた

Crate Trends というサイトの作者の方が LT をしてくださいました。サイトの紹介がメインでした。Rust には数多くのクレートが存在し、たとえば Web サーバーというキーワードに対して複数のクレートが検索結果として出てくるという状況になっています。このサイトを利用すると、クレート同士のダウンロード数やライセンスなどを比較しながら検討できます。

フロントエンドは Next.js で作成し、バックエンドは actix-web で構築したとのことでした。クレートの情報そのものは crates_io_api というクレートを利用したそうです。コントリビューション等をお待ちしておりますとのことなので、ぜひ気になった機能や追加したい機能がある方はカスタマイズしていきましょう!

github.com