0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

make_u32_from_two_u16に学ぶ機能設計

Posted at

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

これを毎回書いた方が見通しが良いんじゃないかな・・。

おちなし。おしまい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?