RustのCrate調査 (version_check, ryu, unicode-width, fnv)

先日、Rust言語でよく使われているcrateをいくつか調べました。

toyamaguchi.hatenablog.com

今回は、version_check、ryu、unicode-width、fnvについて調査をしました。

コンパイラのバージョンは「rustc 1.33.0」です。

version_check : コンパイラのバージョンをチェック

version_checkは、現在の開発環境にあるコンパイラのバージョンチェックを行うcrateです。

注意しなくてはいけないことは、コンパイル時のコンパイラのバージョンではないところです。

このcrateの内部では、コマンドで「rustc --version」のプロセスを起動して、そこからバージョン情報を得ています。

Rustの開発ツールを作るときなど、コンパイラのバージョンに依存したコードを書くときに利用できますね。

サンプルコードは次のようになります。

crateのバージョンは、「version_check = "0.1.5"」です。

use version_check;

pub fn run() {
    println!("##### version_check #####");
    println!("{}", match version_check::is_nightly() { // nightly or not
        Some(true) => "running a nightly",
        Some(false) => "not nightly",
        None => "couldn't figure it out"
    });
    println!("{}", match version_check::is_min_version("1.13.0") { // compare by version
        Some((true, version)) => format!("Yes! It's: {}", version),
        Some((false, version)) => format!("No! {} is too old!", version),
        None => "couldn't figure it out".into()
    });
    println!("{}", match version_check::is_min_date("2016-12-18") { // compare by date
        Some((true, date)) => format!("Yes! It's: {}", date),
        Some((false, date)) => format!("No! {} is too long ago!", date),
        None => "couldn't figure it out".into()
    });
}

出力は次のようになります。

##### version_check #####
not nightly
Yes! It's: 1.33.0
Yes! It's: 2019-02-28

ryu : 浮動小数点数を高速に文字列に変換

ryuは、浮動小数点数を高速に文字列に変換するcrateとのことです。

そんなことにわざわざcrateが必要なのか、と思ってしまいますが、情報工学の素養がある方は、浮動小数点数の扱い方が少しやっかいなことをご存じの方も多いはずです。

どうもドキュメントによると、こちらの論文の技術を利用することで、高速に正確に浮動小数点数を文字列に直すことができるらしいのです。 (Google GermanyのUlf Adams氏の論文のようです。)

ryuの使い方は次のようになります。

use ryu;

pub fn run() {
    println!("##### ryu #####");
    let mut buffer = ryu::Buffer::new();
    println!("{}", buffer.format(1.234));
    println!("{}", buffer.format(0.000001));
    println!("{}", buffer.format(-0.000001));
    println!("{}", buffer.format(std::f32::INFINITY));
    println!("{}", buffer.format(std::f32::NAN));
}

出力は次のようになります。

##### ryu #####
1.234
1e-6
-1e-6
1.0737418e40
1.6106127e40

ドキュメントにも書いてありますが、無限 (std::f32::INFINITY)、NaN (std::f32::NAN)には対応していないので、気にする場合は直前にis_finite()やis_nan()で確認が必要です。

unicode-width : Unicode文字列の文字幅を計算

Unicode文字列の表示幅を計算してくれるcrateです。

Unicode文字列の場合、実際の表示幅とバイト数が異なるため、このようなcrateが必要になります。

サンプルコードは次のようになります。

UnicodeWidthStrはstrに対するのtraitになっています。

use unicode_width::UnicodeWidthStr;

pub fn run() {
    println!("##### unicode_width #####");
    let teststr = "ハローワールド";
    println!("{}", teststr);
    println!("The above string length is {}.", teststr.len());
    println!("The above string is {} columns wide.", teststr.width());
    println!("The above string is {} columns wide (CJK).", teststr.width_cjk());

    let teststr = "Hello, world!";
    println!("{}", teststr);
    println!("The above string length is {}.", teststr.len());
    println!("The above string is {} columns wide.", teststr.width());
    println!("The above string is {} columns wide (CJK).", teststr.width_cjk());
}

出力は次のようになります。

##### unicode_width #####                 
ハローワールド
The above string length is 21.
The above string is 14 columns wide.
The above string is 14 columns wide (CJK).
Hello, world!
The above string length is 33.
The above string is 23 columns wide.
The above string is 23 columns wide (CJK).

日本語を画面に表示するときの、レイアウトの調節に使えそうですね。

fnv : Fowler–Noll–Vo ハッシュ関数

このcrateは、Fowler–Noll–Vo ハッシュ関数の実装(HashMap、HashSet)が含まれているcrateです。

Fowler–Noll–Voというのは、このハッシュ関数を作った3人の名前 (Glenn Fowler、Landon Curt Noll、Kiem-Phong Vo)が由来だそうです。

どうもデフォルトのHashMapは、integer型のような小さいデータ型をキーとして使った場合、処理が遅いようです。

たいていの場合、キーはそれほど大きくないと思います。そのような場合には、このfnvを使ったほうが高速に処理ができるらしいです。

ただし、キーが大きい場合には、fnvはとても遅いらしいので、使い分けに注意して使いましょう。

crates.ioのfnvのページにリンクが貼ってあった、ハッシュ関数の比較ページの中にグラフが貼ってあったので、そちらを参考にしてください。

f:id:toyamaguchi:20190403133336p:plain
Comparison of Hash Function

サンプルコードはシンプルです。

use fnv::FnvHashMap;

pub fn run() {
    println!("##### fnv #####");
    let mut map = FnvHashMap::default();
    map.insert(1, "one");
    map.insert(2, "two");
    println!("{} {}", map.get(&1).unwrap(), map.get(&2).unwrap());
}

出力は次のようになります。

##### fnv #####
one two

使い方は普通のHashMapと変わりませんね。

まとめ

今回は、version_check、ryu、unicode-width、fnvについて調査を行いました。

また、気になるcrateがあったら調査していこうと思います。

参考文献