GoとRustの比較

こういうバージョンの処理系で試した。

$ go version
go version go1.1.2 linux/amd64
$ rustc --version
rustc 0.9-pre (825b127 2013-11-12 18:56:13 -0800)
host: x86_64-unknown-linux-gnu
// Go

package main

import (
	"fmt"
	"sort"
	"strconv"
)

type T struct {
	int
	string
}
type U []*T

func (s U) Len() int {
	return len(s)
}

func (s U) Less(i, j int) bool {
	return s[i].int < s[j].int
}

func (s U) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func main() {
	ch := make(chan *T)
	for i := 0; i < 100; i++ {
		go func(i int) {
			var b string
			switch x, y := i%3, i%5; true {
			case x == 0 && y == 0:
				b = "FizzBuzz"
			case x == 0 && y != 0:
				b = "Fizz"
			case x != 0 && y == 0:
				b = "Buzz"
			default:
				b = strconv.Itoa(i)
			}
			ch <- &T{i, b}
		}(i + 1)
	}

	res := make(U, 0, 100)
	for i := 0; i < 100; i++ {
		res = append(res, <-ch)
	}

	sort.Sort(res)

	for _, v := range res {
		fmt.Println(v.string)
	}
}
// Rust 0.9-pre

extern mod extra;

fn main() {
	let (port, chan) = stream();
	let chan = std::comm::SharedChan::new(chan);
	for i in range(0, 100) {
		let chan = chan.clone();
		do spawn {
			let i = i + 1;
			let b = match (i % 3, i % 5) {
				(0, 0) => ~"FizzBuzz",
				(0, _) => ~"Fizz",
				(_, 0) => ~"Buzz",
				_      => i.to_str()
			};
			chan.send((i, b));
		}
	}

	let mut res = ~[];
	do 100.times {
		res.push(port.recv());
	}

	extra::sort::quick_sort(res, |t, u| t.first() <= u.first());

	for t in res.iter() {
		std::io::println(t.second());
	}
}

Goはダックタイピングでゆるふわ系で、Rustは厳密に型付けして型推論バリバリ系。

Rustの側はデータの受け取りをタプルで済ませていたり、比較用関数はラムダ式で渡せば良かったりするので短くなっている。Goはtypeで雑多な型を宣言した上で、雑多な型に対してSort用のメソッド定義している。面倒臭い。

Rustのmatchは式なんだけど、Goのswitchは文なので、変数bの宣言を前に出すはめになってしまい、無駄に型を書くことになった。

forループの変数のスコープ、Goの場合はループ全体で同じ変数なので、go文に渡す関数の引数を介してやらないと正しく動きません。Rustはループ毎に別なのでそのまま使って大丈夫。

ビルド時間はGoのが断然はやい。

$ time go build fizzbuzz.go

real	0m0.185s
user	0m0.146s
sys	0m0.038s
$ time rustc fizzbuzz_0.9.rs

real	0m1.215s
user	0m1.092s
sys	0m0.119s

バイナリの実行速度は、このコードで比較するのはやめておきます……