開発

[binllion] 重複処理をまとめる

前回にてASCII変換処理はオリジナルの関数to_ascii_9()を使用することで落ち着きました。

次にこの関数を呼び出すことを考えましょう。

16進数に変換するto_hex()と同じ呼び出し方にすれば良いので下記のようになると思います。

pub(crate) fn to_hex(buf: &[u8]) -> String {
    let sep = String::from(" ");
    let hex = buf
        .iter()
        .map(|x| format!("{:02X}", x))
        .collect::<Vec<_>>()
        .join(&sep);
    // dbg!(&hex);
    hex
}

pub(crate) fn to_lines(buf: &[u8], len: usize) -> Vec<Line> {
    let mut vec = Vec::new();
    buf.chunks(len).for_each(|x| {
        vec.push(Line::from(format!(
            "{:width$} {}",
            " ",
            self::to_hex(x),
            width = 8
        )))
    });
    // dbg!(&vec);
    vec
}

pub(crate) fn to_ascii_9(buf: &[u8]) -> String {
    let res: String = buf
        .iter()
        .map(|&x| format!("{}", ToPrintableAscii::as_printable_char(x)))
        .collect();
    res
}

pub(crate) fn to_lines_ascii(buf: &[u8], len: usize) -> Vec<Line> {
    let mut vec = Vec::new();
    buf.chunks(len).for_each(|x| {
        vec.push(Line::from(format!(
            "{:width$} {}",
            " ",
            self::to_ascii_9(x),
            width = 8
        )))
    });
    // dbg!(&vec);
    vec
}

関数名以外は記述はほぼ同じです。呼び出す関数名も変化しただけです。

このまま使っても動作はするでしょうが機能追加の度にコードが大きくなっていきそうです。

to_lines()に変更を加えて引数に関数を受け取れるようにしてみます。テストがややこしくなるので一時的にVec<Line>ではなくVec<String>を返すようにしています。

// convert_to_ascii

// 型制約を使う
pub(crate) fn to_lines5(buf: &[u8], len: usize, func: fn(&[u8]) -> String) -> Vec<String> {
    let mut vec = Vec::new();
    buf.chunks(len)
        .for_each(|x| vec.push(format!("{:width$} {}", " ", func(x), width = 8)));
    vec
}Code language: JavaScript (javascript)

新たにfuncという引数を追加しました。

引数の型としてfn(&[u8]) -> Stringとしました。これでどちらの関数も同じto_lines()から呼べるようになりました。

// convert_to_ascii

    #[test]
    fn test_to_lines_with_hello_2() {
        let bin_data = setup();
        let expect = setup_to_ascii();
        let res = to_lines5(bin_data, 8, to_ascii_9);
        dbg!(&res);
        println!("{}: {:?}", "to_ascii_9", &res);
        assert_eq!(res, expect);

        let expect = setup_to_lines_hex();
        let res = to_lines5(bin_data, 8, to_hex);
        dbg!(&res);
        println!("{}: {:?}", "to_hex", &res);
        assert_eq!(res, expect);
// 略Code language: PHP (php)

注意点

ただし、これには問題があるかもしれません。

fn(&[u8]) -> Stringを満たす適当なクロージャを引数として渡しても動作します。

例えば、このよう形でも動作します。

// convert_to_ascii

        let res_1 = to_lines5(bin_data, 8, |_| -> String { "hello".to_string() }); // 外部変数をキャプチャしないと関数にキャストされる
        /*
            let res_1 = to_lines5(bin_data, 8, |_| -> String {bin_data;"hello".to_string()});  // 外部変数をキャプチャするとクロージャとして扱われて型がマッチせずエラー
        */
        dbg!(&res_1);
        println!("{}: {:?}", "hello", &res_1);
Code language: JavaScript (javascript)

クロージャを渡せるので手軽にコードを書ける柔軟性がありますが、変数をキャプチャしてしまうとエラーとなり、何が原因となっているのか分かりづらくなります。

Rustの自動変換が働いているのですが、初見殺しなコードになる可能性があります。

GitHubにコードをアップロードしています。0.1.0バージョン

コードのコメントに書かれているfirst_stepなどをcargoコマンドに渡すと実行できます。

# Example
$ cargo run --example first_stepCode language: Bash (bash)
管理人

Recent Posts

情報セキュリティマネジメント試験取得への道

スキルアップを図るべく情報セキ…

2か月 ago

ファイナンシャルプランナー3級試験取得への道

スキルアップを図るべくファイナ…

2か月 ago

[rust] New Type Patternを使ってみる

DDDの考えを取り入れることで…

5か月 ago

RustでDDDの要素を取り入れてみる

前回SOLID原則というものを…

5か月 ago

期間限定!書籍無料キャンペーン2025

「mdBookではじめるKin…

5か月 ago