読者です 読者をやめる 読者になる 読者になる

rust-carbonっていうライブラリ作った

先月ようやく rust 1.0 が出たということで、いじってみるがてらライブラリをひとつ作った。

rust-carbon という名前で、rust で時間の扱いをやりやすくするものだ。

インターフェイスphpcarbon という有名なライブラリを真似たので、それが名前の由来となっている。以下使い方とか書いていく。

インストール

Cargo.toml にこれ書いて

[dependencies]
carbon = "0.1.*"

こう

extern crate carbon;

使い方

こんな感じ。現在時刻が 2015-01-15 01:30:30.500000000 だとして考えて欲しい。

// インポート
use carbon::*;

// 現在時刻は 2015-01-15 01:30:30.500000000

DateTime::now().start_of().second();
// => carbon::DateTime に 2015-01-15 01:30:30.000000000 が詰まって返る
DateTime::now().start_of().minute();
// => carbon::DateTime に 2015-01-15 01:30:00.000000000 が詰まって返る
DateTime::now().start_of().hour();
// => carbon::DateTime に 2015-01-15 01:00:00.000000000 が詰まって返る
DateTime::now().start_of().day();
// => carbon::DateTime に 2015-01-15 00:00:00.000000000 が詰まって返る
DateTime::now().start_of().month();
// => carbon::DateTime に 2015-01-01 00:00:00.000000000 が詰まって返る

DateTime::now().end_of().second();
// => carbon::DateTime に 2015-01-15 01:30:30.999999999 が詰まって返る
DateTime::now().end_of().minute();
// => carbon::DateTime に 2015-01-15 01:30:59.999999999 が詰まって返る
DateTime::now().end_of().hour();
// => carbon::DateTime に 2015-01-15 01:59:59.999999999 が詰まって返る
DateTime::now().end_of().day();
// => carbon::DateTime に 2015-01-15 23:59:59.999999999 が詰まって返る
DateTime::now().end_of().month();
// => carbon::DateTime に 2015-01-31 23:59:59.999999999 が詰まって返る

当然メソッドチェーンできる

DateTime::now().start_of().day().end_of().hour();
// => carbon::DateTime に 2015-01-15 00:59:59.999999999 が詰まって返る

単体テストなどで now の返り値を指定したい場合があると思う。rust-carbon では以下のように指定できる。

// 例えば現在時刻を 2015-01-01 00:00:00 にしたいとする
let tm = time::Tm {
  tm_sec: 0, tm_min: 0, tm_hour: 0, tm_mday: 1, tm_mon: 0, tm_year: 115, tm_wday: 4, tm_yday: 0, tm_isdst: 0, tm_utcoff: 0, tm_nsec: 0 };
};

let test_now = DateTime::create_from_tm(tm);
DateTime::set_test_now(test_now);
DateTime::now();
// => carbon::DateTime に 2015-01-01 00:00:00 が詰まって返る

また、rust-carbon は rust の準標準ライブラリである rust-time の薄いラッパーである。

そのため、rust-time の構造体及びそこに生えているメソッドに簡単にアクセスできる。to_string() とか細かい時間の調節とかはそっちのメソッドを使ってもらうことになる。

// 現在時刻は 2015-01-15 01:30:30.500000000

DateTime::now().tm.strftime("%Y-%m-%d %H:%M:%S").ok().unwrap().to_string();
// => "2015-01-15 01:30:30" という string が返る

use std::ops::{Add, Sub};

let hour = time::Duration::hours(1);
DateTime::now().tm.add(hour)
// rust-time の time::Tm 構造体に 2015-01-15 02:30:30.500000000 が詰まって返る

他詳細なことについては README を見てほしい。

補足

rust-carbon は習作で、冒頭にも書いたとおり、とりあえず rust 1.0 を触ってみたかったという動機から作られている。実用を目的としたわけではないので、実際に使おうとすると困る点がいくつかある。具体的には、まず UTC 以外の時間が扱えない(日本人だと絶望的)。あと time::Tm.add()/sub() を使った時に time::Tm が返ってしまうので、細かい時間調節をしようとすると小回りが効かない。

この辺りは気が向いた時とか実際に使い始めたとかされたら直していきたい。

また、rust で時間を扱うライブラリとしては、rust-chrono もある。rust-carbon はシンプルなのに対して、rust-chrono は複雑なことができる。 まぁ個人的には rust-chrono はインターフェイスがゴチャゴチャしていて気にくわないし、元々 rust-time にかなり任せられそうなので、あんな大仰で面倒くさそうなラッパーいるか?というのが正直な気持ちだが、もし rust で便利な時間操作系のライブラリを探しているとかいうのであれば、検討すると良いと思う。

rust 1.0 の雑感

rust は地味に nightly の頃にいじってみていたのだが 当時作ったライブラリは何にもいじってなくてもデイリーでビルドがぶっ壊れていてなかなか愉快だった。 公式ドキュメントの説明も難しかったし、まともな rust のコード読もうとすると rust 本体のコードしかないみたいな感じだった。

正直 1.0 も見送って 1.1 になるまで触るのやめようかなと思っていたのだが、今回の 1.0 でも触っていて先に挙げていたようなストレスはほとんどなくなっていた。ビルドもぶっ壊れなくなったし、今回の公式ドキュメント もだいぶ読みやすくなったと思う。

rust はモダンな言語仕様をふんだんに取り入れていて、本来持っているポテンシャルは高いと思う。細かいところはいちいち説明しきれないのでぜひ自分で見てくれという感じだが、結構堅牢でカッコイイ実装ができる。その上 ownership,borrow,lifetime の概念により省メモリに抑えることができる。結果として堅牢さと軽さが両立するのでかなりいい言語なのではという気がする。触っていてなかなか楽しかった。

ただ、go みたいな手軽さはほとんどない。サッと書いて動かして感動を得るというよりは、堅牢に設計した時の俺スゲー感とか、メモリチューニングを楽しんでいく言語だと思う。(go との比較ついでに書くと、並列処理書くのがまだ面倒なのが痛いが)

ちなみに、rust を今現在の仕事の現場で使いたいかというとそれはNOになる。あんまりぶっ壊れなくなったとはいえヘビーに触ったわけではないのでまだ怖いし、そもそも書く人が多いとはいえない言語なので現場で導入というのは考えられない。

言語本来のポテンシャルとしては他人と一緒に開発するのに向いているし、これから伸びていったら面白そう。