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

テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(writeboost report) dm-writeboost-tools v1をリリースした

github.com

ライトブーストの管理ツールであるdm-writeboost-toolsのv1をリリースしました. ご査収ください.

コマンドラインライブラリは, 古めかしいgetoptsを棄ててclap-rsを使いました. キャッシュデバイスにlittle endianで書かれた値を読み出すにはbyteorderというライブラリを使いました. Rustは, 標準ライブラリは小さくしてサードパーティのライブラリでなんとかしていくOCamlスタイルです. しかしOCamlと違うのは, そのエコシステムがとてもうまく回っているところです. また, Rustを書こうとするプログラマは他の言語をいくつも経験していたり, かなりレベルの高いプログラマなので, どのライブラリも質の高いものが高速に出来上がっていく傾向にあります. やっぱRustが最強のソリューションですね.

コマンドの説明:

  • wbcheck: ログが正しく書かれているかチェックサムを再計算して照合します
  • wbcreate: ライトブーストデバイスを作ります
  • wbdump: 特定のキャッシュブロックをダンプします
  • wbmeta: ログのメタデータをHuman-readableにダンプします
  • wbremove: ライトブーストデバイスを削除します
  • wbstatus: dmsetup statusの出力をpretty printします

3分程度の簡単な動画をとったのでご覧ください.

www.youtube.com

次のRustネタどうしよっかなーw

(rust report) 寿命は気にせずownしてカジュアルに使う

Rustを書こうと思った時, いきなり完璧なRustコードを書こうと思うとRustは入門すら不能になる. 特に寿命についていきなり最適を目指そうとするのは, かなり疲れる. おそらく寿命に関するコンパイルエラーによって統合か頭髪のいずれかを失う. そこでみなさんにやすらぎのツイートを紹介する.

おれもこれは思っていて, 「最悪ownしてしまえば寿命に悩まされることはない」ということが分かっていれば, Rustに入門するのはそんなに難しくない. これは例えるならば, 泳ぐのが怖いと思ってる人でも, 最悪立ってしまえば安全と分かっていれば泳ぐことが怖くなくなるようなものだ.

もちろん, 可変を前提としたデータ構造などロジック的にコピーしてはならないオブジェクトが存在することはあるが, そういうものはそもそもコピーするコストが膨大だからソフトウェア設計上可変になってるのであって, 不変なオブジェクトは大抵は小さく, 頻繁にコピーしていったところでソフトウェア全体が急激に遅くなるということはありえない.

例えば, Rustは文字列について&strとStringの選択があるが, これがまぁまぁのややこしさを生むことがある. だったら最初からto_stringしてしまってownしてしまう方が楽だと思う. 小さいオブジェクトは作っては消えてを繰り返すことが多いから, 変に参照でがんばろうとすると寿命関連でコンパイルエラーになる可能性が高いのだと考えている. 副次的な効果として, structに寿命パラメータがついたり, ぐちゃぐちゃになることを防ぐことが出来て, コードがわかりやすくなるメリットもある.

// to_stringはここに帰着する
impl<'a> From<&'a str> for String {
    fn from(s: &'a str) -> String {
        s.to_owned()
    }
}

もし, ownしていることが性能上問題だと分かったら, その時に直せばいい. 最適な寿命パラメータを与えていくことは, おそらく機械的に出来るし, なにより, ロジックを壊すことはコンパイラが防いでくれるから安全にリファクタリングすることが出来る.

でもおれとしてはこんなことは本来Rustの言語本体ががんばることではないのかと思う. 例えばRustにはCow型がある. これは本当に必要になるまではownしませんという型だが, これをデフォルトになってないのはなぜだろうか.

(writeboost report) dm-writeboost-toolsをRustで書き直しました

www.slideshare.net

で, dm-writeboostのツールをRustで書きますと言ってたのに, チッめんどくせーなと思ってGoで書いてしまってから早くも半世紀が経ちました.

難しいプログラムではないので, 作るだけなら当時でも出来たと思うんだけど(coreutilsをやったわけだし), 次にRustを書く時はちゃんと勉強して納得した上でにしたいなと思ってたので, とりあえずGoで書き散らしました. だけど, おれはGoが本当に好きじゃないので, プログラムはとりあえず動くだけでそれ以上オプションなどを拡張していく気が一切起きませんでした.

Rustの本を読んだりして, ざっくりは分かったなという気持ちになり

f:id:akiradeveloper529:20170117101501j:plain

というかこんな感じで「何でも出来る」という気持ちになったため, Rustで書き直しました.

github.com

このツールセットは, ライトブーストのメタデータの状況を見たり, ログが壊れてないかチェックするためのものですが, ユーザがライトブーストの挙動を確認するためにも使えます. 今後は, 新規にwbcreate, wbremoveも作っていきます. 結構嵌るユーザが多いので, コマンドを被せて嵌まらないようにしてあげたいです.

この書き直しだけでも色々ライブラリを見たり, Rustの基本文法についても習熟する良い演習問題になったので良かったと思います. みなさんもGoで書いてしまったファッキンプログラムをRustで書き直しましょう.

ココナッツオイルなしじゃ生きていけない

f:id:akiradeveloper529:20170116194229j:plain

ミックスナッツシェイク. 一番上に乗ってるアイスクリームみたいな塊は何だかわかりますか?ココナッツオイルです. たぶん100g近い. 従って900kcal近い. トータルでは1000kcal余裕で超えているでしょう.

糖質制限の最高のパートナーがココナッツミルクです. これなしでは糖質制限は成り立たないという結論に至ったので毎日いっぱい食べてます.

ココナッツミルクは, ミランダ・カーも食べてます. 彼女は一日50gくらい食べる. だからミランダは美しいのね. ミランダは「ココナッツオイルないじゃ生きていけない」って言ってる. おれもだ. ミランダとは仲良くやっていけそう.

(rust report) mut&はなくていい

akiradeveloper.hatenadiary.com

は解決した. κeenさんという人がTwitterで教えてくれた.

"readは&mut selfを要求するから&mut &[u8]を渡さないといけないだけの話"で, 型の話はわかった.

これは知らなかった. というかどっかに書いてあった気もするが, 理解してなかった.

これに照らし合わせると,

    let mut vv: &[u8] = &v[..];
    vv.read(&mut b).unwrap();

というのは,

  • vvは&[u8] (ファットポインタ)型の値を, 変更可能を宣言してる変数に束縛した
  • vv.readは, auto-borrowingでvvを&mutでborrowしている

と考えることが出来る.

商品がなかなか発送されないのでアマゾンに問い合わせた

アマゾンは尋常じゃないカスタマーセンターを持っており, 電話が欲しいと指定したら10秒以内に電話がかかってきた.

まだ発送されていない商品はRazer Tartarus Chroma. 他に買ったRazer DeathAddrも同様にAmazon.jp G.K.から買っているので, 常識に考えると同時に届くべき. だけどDeathAddrだけ届いたのでやばいトラブルなのではないかと思ってクレームした.

原因はアマゾンの受注システムの糞仕様にある. アマゾンは在庫が10個しかない場合でもそれ以上の受注を受けつけてしまうようだ. つまり, アマゾンの注文ページに書いてある在庫いくつですというのはリアルタイムではない.

その結果, おれの発注を配送する時には在庫が切れていて, 今は入荷待ちとなってしまったということのようだ. 購入時点では「ある」と言っておいて実際には「ない」のだから, 明らかな詐欺行為である.

死ねと言いたい. おれは明日からマウサーとしてキルしまくる予定だったのに. 土日もまたパパパパパッドかよ!!

ヨドバシカメラを探してあったら即キャンセルする.

なお, カスタマーセンターの電話担当は全くダメだった. まず, 何を言ってるのかはっきりせず, すごくイライラした. 言ってることが杓子定規で, 何を根拠にそんなことが言えるのか分からないということも多かった. また, 声のトーンも異常に高く, バカなのかと思った. クレーム対応には声のトーンも含めて技術が確立されている. 勉強してほしい.

www.youtube.com

(rust report) mut&は何故ないのか?

分かる人がいたら教えてくれシリーズ. (Twitter/Mailでマサカリを投げてきて) 以下では言い切ってるところも多いが, ほとんどの場合, おれの推察である.

まず議題となるコード. これがなぜダメなのかおれなりに考える. このコードは, vから先頭2つをbにreadした時にvはどういう状態になるか?を確かめる過程で生まれた. 結論としては[2,3,4]になる. それがreadの挙動らしい.

use std::io::Read;
fn main() {
    let mut v: Vec<u8> = vec![0,1,2,3,4];
    let mut b = vec![0;2];
    let vv: &mut[u8] = &mut v[..];
    vv.read(&mut b).unwrap(); // これはコンパイル通らない
}

このコードが謎なのは, readの型が

pub trait Read {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize>;

なのに,

error: no method named `read` found for type `&mut [u8]` in the current scope
 --> src/main.rs:8:8
  |
8 |     vv.read(&mut b).unwrap();
  |        ^^^^
  |
  = note: the method `read` exists but the following trait bounds were not satisfied: `[u8] : std::io::Read`

と怒られることだ. vvの型は&mut[u8]なので, readはコンパイルが通っても良さそうなものだが.

ちょっと, vvの作り方を変えよう.

use std::io::Read;
fn main() {
    let mut v: Vec<u8> = vec![0,1,2,3,4];
    let mut b = vec![0;2];
    let mut vv: &[u8] = &v[..];
    vv.read(&mut b).unwrap();
}

こうするとうまく行く. 今度はvvの型は&[u8]だが, variable自体がmutになっている点がさきほどと異なる.

なぜか?

試しに第二引数を第一引数と同様の方式で作ってみよう.

use std::io::Read;
fn main() {
    let mut v: Vec<u8> = vec![0,1,2,3,4];
    let mut b = vec![0;2];
    let mut vv: &[u8] = &v[..];
    let mut bb: &[u8] = &b[..];
    vv.read(bb).unwrap(); // これはコンパイル通らない
}
error[E0308]: mismatched types
 --> src/main.rs:9:13
  |
9 |     vv.read(bb).unwrap();
  |             ^^ types differ in mutability
  |
  = note: expected type `&mut [u8]`
  = note:    found type `&[u8]`

今度は, &mut [u8]が要求されているが, &[u8]を渡されたと怒られた.

これらは, readが実はread(self: mut &[u8], &mut [u8])というシグネチャなのだと考えると理解出来る.

しかし何らかの理由で, Rustにはmut &という書き方は存在しない. これはおそらく, この2つがRustの型システムの中で全く別モノとして扱われていて混同することがないからだ. 言い換えると, 片方が必要な時にもう片方がたまたまコンパイルを通ってしまうことがないからだ. 実際にこれらのエラーメッセージは明らかに, &mut [u8], &[u8]というものを型として扱っている. Rustがmutabilityや所有権/Borrowを型として認識しているという証拠に, BorrowやBorrowMut, Fn/FnMut/FnOnceなどが定義されていることが挙げられる.

メモリレイアウトの観点からそれぞれのletが何を意味してるのかを考えてみよう.

let mut v: Vec<u8> = vec![0,1,2,3,4];

は, ヒープにある領域(配列)とスタックフレームにある(ヒープ領域へのポインタ, capacity, length)のファットポインタについてrwする権限を持つということ.

let mut vv: &[u8] = &v[..];

は, そのヒープ領域に対するread onlyなファットポインタ(ポインタ, length)がwritableである.

let vv: &mut[u8] = &mut v[..];

は, そのヒープ領域に対するwritableなファットポインタがread onlyである.

つまり, どこがwritableかがお互いに排反だから, あらゆるケースにおいて片方がもう片方を代替することがないということなのだ.

おそらく通常のケースにおいて, let mut = &の形をとることには意味がない. 意味があるのはVecやsliceを扱う時だけであり, これらの型に対してRustがスタック上のメモリレイアウトを特殊扱いしているということだ.

let mut = &がスタック上のオブジェクトに対してwritableであるということは,

fn main() {
    let v: Vec<u8> = vec![0,1,2,3,4];
    let mut vv = &v;
}

コンパイルが通ることでも分かる. vがmutでないにも関わらず, vvはmutで定義出来てしまっている. これならば明らかに, vのメモリはread onlyに守られていて, vvの部分だけがwritableであるということが分かる.

いずれにしろ, mut &Tという型を書けるようにした方がわかりやすいのではないかと思う.