make_u32_from...
リーナスぶちギレ案件としてmake_u32_from_two_u16
は記憶に新しいところです、要するに引数a, bとかあるけどどっちが上位でどっちが下位かわからないからミス増えるんじゃねーの?この**がっ! っていう主張だったわけです。でも感慨深い。
そもそもそんなものマクロとかにしなくても簡単に書けるのになんでそんなもの書いちゃったんだというのはさておいて扱いやすいヘルパー関数をRustで作ることを考えてみます。
楽をしたいのでここではConv16
という8bitの値二つを16bitに変換するというものにします。
Rustといえば構造体
Rustで変なことに頭を抱えて悩まない為にも構造体は使うべきだし慣れてしまえば手放せないほど便利
しかしこういうヘルパー関数みたいなものの構造体をいちいちインスタンス初期化して使いたくないよなー、めんどくさいよなー。となります。
とはいえそれは仕方ないことなのでせめてメソッドチェーンで楽をします。
#[allow(dead_code)]
#[derive(Copy, Clone, Debug, Default)]
pub struct Conv16 {
h: u8,
l: u8,
}
#[allow(dead_code)]
impl Conv16 {
pub fn new() -> self {
Self::default()
}
pub fn high(mut self, h:u8) -> Self {
self.h = h;
self
}
pub fn low(mut self, l:u8) -> Self {
self.l = l;
self
}
pub fn convert(self) -> u16 {
((self.h as u16) << 8) | (self.l as u16)
}
}
単純な構造体とその関数ですが、Convert(a, b)
などという関数に比べれば上位や下位がはっきりしていて分かりやすいのではないでしょうか。
let v = Conv16::new().high(0x12).low(0x34).convert();
という感じで利用できます。当然 highとlowは入れ替えても大丈夫。
考えられる問題点
例えばこの Conv16 ですが次のようにも利用できます。
let c = Conv16::new();
let v = c.high(0x12).convert();
答えとしては 0x1200 が返ってくるのですが、それは問題ありません。
問題があるのはその後 インスタンス c
を利用し続けられることにあります。
動作としてはconvertを実行するたびに中身は0になりますので
let c = Conv16::new();
let v = c.high(0x12).convert();
println!("0x{:04x}", v);
let v = c.low(0xff).convert();
println!("0x{:04x}", v);
このようなコードを実行した場合答えは次のようになります。
0x1200
0x00ff
これの何が問題かというと「理解しづらい」のです。
期待している2番目の答えは0x12ff
かもしれないけれどコードにヒントが無さすぎるのです。
ならばいっそConv16のインスタンスは使いまわせない方が良いかも知れません。
構造体のアトリビュートを変更します。
#[allow(dead_code)]
#[derive(Clone, Debug, Default)] // Copyを削除した
pub struct Conv16 {
h: u8,
l: u8,
}
アトリビュートをこのようにしておくことで先ほどのようなコードを実行すると所有権の問題でエラーが発生するようになります。
インスタンスを使いまわせなくなったため正しい使い方は次のようになります。
let v = Conv16::new().high(0x11).convert();
println!("0x{:04x}", v);
let v = Conv16::new().low(0xff).convert();
println!("0x{:04x}", v);
let v = Conv16::new().low(0x34).high(0x12).convert();
println!("0x{:04x}", v);
毎回 newしているのだから初期化されていて当然です、見通しが良くなったのではないでしょうか。
さらなる発展とか妄想とか
いい感じに関数が出来たところで欲望が芽生えてきます。
毎回インスタンス初期化してるの気持ち悪いからやめたいな。などと。
別の構造体でビルダーを作って highやlowがnewの働きをすれば一見インスタンスを生成しているようには見えなくなるかもしれませんしマクロにすればきれいに書けるかもしれません。無名関数?まあそれもいいような気もします。
linuxのカーネルがらみの話などこんなレベルではないんでしょうけれどもどうやったら素敵な関数になるかなど考えてみると面白いものですね。
しかしこの度のmake_u32_from_two_u16
については…
a << 16 | b
これを毎回書いた方が見通しが良いんじゃないかな・・。
おちなし。おしまい。