pretty code

顯示具有 Rust 標籤的文章。 顯示所有文章
顯示具有 Rust 標籤的文章。 顯示所有文章

2019年7月3日 星期三

Rust 探索之旅 - 函數回傳值

使用 return 就跟平常 C 語言用法一樣。

我們也可以不使用 return,但是傳遞回傳值的那一行不用加分號。

fn main() {
    let s1 = String::from("hello");

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let n = s.len();
    (s, n)
}

2019年6月28日 星期五

魔術數字 0xED3A65B4CB4B34E9

Hamming (72,64) 演算法中,目前知道 extra parity 8 有 2 種算法:
1. XORing all data bits.
2. step1 plus XORing parity 1 ~ parity 7.

我之前用的都是 1 的作法,知道 2 的作法後,終於解答了 0xED3A65B4CB4B34E9 這個魔術數字的算法,Rust 的 sample code 如下:

fn main() {
    let p1: u64 = 0xDAB5556A_AAAAAAD5;
    let p2: u64 = 0xB66CCCD9_999999B3;
    let p3: u64 = 0x71E3C3C7_8787878F;
    let p4: u64 = 0x0FE03FC0_7F807F80;
    let p5: u64 = 0x001FFFC0_007FFF80;
    let p6: u64 = 0x0000003F_FFFFFF80;
    let p7: u64 = 0x00000000_0000007F;
    let p8: u64 = 0xFFFFFFFF_FFFFFFFF;

    let another_p8 = p1 ^ p2 ^ p3 ^ p4 ^ p5 ^ p6 ^ p7 ^ p8;

    println!("0x{:X}", another_p8);
}


2019年6月26日 星期三

Rust 探索之旅 - 撞牆期開始

到目前為止,我最喜歡 Rust 的一個地方是它可以使用 "_" 來分隔數字,可以當千分位號使用,也可以分隔 16 進位數字,後者應該是我們程式設計師最常使用的。

最近在整理上禮拜看的一個演算法,其中有一些魔術數字,由於位數太多,實在很容易在 porting code 時出錯,剛好我已經完成 C 語言的版本,故想使用目前為止會的 Rust 來實作看看,殊不知開始進入了瘋狂 google 的地獄輪迴。

細節就不多說了,總之就是花了一個早上,才寫出這個 100 行的程式,開始有點懷念 Go 了,雖然我也很久沒寫了,距離上一次使用 Go 寫測試程式也有一年多了吧?

2019/06/27 更新
使用 rustfmt 重新排版 code。

重點提示
01. 函數只能有一個回傳值,參數要寫型別。
02. Data Type 真的有一種型別叫 "usize"。
03. 型別不符時,可以使用 "as" 轉型。
04. "print!" 不會換行,故可能會存在 Buffer 無法印出,使用 flush 清空 Buffer。

use std::io;
use std::io::Write;
use std::u64;

const BIT_TABLE: [u8; 16] = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4];

fn get_parity_bit(data: u64) -> u8 {
    let mut odd_bit_count: u8 = 0;
    let mut step: u8 = 0;

    let mut n: u64 = data;
    let mut index: u64;

    for _i in 0..16 {
        n = n >> step;

        index = n & 0x00000000_0000000F;
        odd_bit_count += BIT_TABLE[index as usize];

        step = 4;
    }

    if odd_bit_count % 2 == 0 {
        return 0;
    }

    return 1;
}

fn get_parity(data: u64) -> u8 {
    let p1 = get_parity_bit(data & 0xDAB5556A_AAAAAAD5);
    let p2 = get_parity_bit(data & 0xB66CCCD9_999999B3);
    let p3 = get_parity_bit(data & 0x71E3C3C7_8787878F);
    let p4 = get_parity_bit(data & 0x0FE03FC0_7F807F80);
    let p5 = get_parity_bit(data & 0x001FFFC0_007FFF80);
    let p6 = get_parity_bit(data & 0x0000003F_FFFFFF80);
    let p7 = get_parity_bit(data & 0x00000000_0000007F);
    let p8 = get_parity_bit(data & 0xFFFFFFFF_FFFFFFFF);

    println!("p1 = {}", p1);
    println!("p2 = {}", p2);
    println!("p3 = {}", p3);
    println!("p4 = {}", p4);
    println!("p5 = {}", p5);
    println!("p6 = {}", p6);
    println!("p7 = {}", p7);
    println!("p8 = {}", p8);

    // The MSB is p8
    let parity_bit = p8 << 7 | p7 << 6 | p6 << 5 | p5 << 4 | p4 << 3 | p3 << 2 | p2 << 1 | p1;

    return parity_bit;
}

fn main() {
    let mut num: String;

    let mut data: u64;
    let mut p: u8;

    loop {
        print!("Please input hex integer(8 byte - 8F7F6F5F4F3F2F1F): ");
        io::stdout().flush().expect("Can't flush buffer");

        num = String::new();
        io::stdin()
            .read_line(&mut num)
            .expect("Failed to read line");

        num = num.trim().to_string();

        if num == "q" {
            println!("Bye Bye");
            break;
        }

        if num.len() != 16 {
            println!("Please input hex integer with 16 {} {}", num.len(), num);
            continue;
        }

        println!("");

        data = u64::from_str_radix(&num, 16).expect("Failed to convert to integer");
        p = get_parity(data);

        println!("--------------------------------------");
        println!("data   = 0x{:016X}", data);
        println!("parity = 0x{:02X}", p);
        println!("-------------------------------------- \n");
    }
}

2019年6月19日 星期三

Rust 探索之旅 - 變數宣告

Rust 是靜態型別的程式語言,理論上我們在宣告變數時,需要寫出型別。

不過,compiler 會跟據後面的值來自動推導型別,故我們可以省略型別宣告。將型別放在變數名稱後面,是幫助 compiler 更容易的去 parsing 原始檔。

let num = 32;
let num: u8 = 32;

另外,我們會用一種 shadow 的技巧,來避免使用太多變數名稱。注意,第 1 個 money 和第 2 個 money 是不同的變數,第 1 個 money 會被第 2 個 money 屏蔽。這裡我們不能在第 1 個 money 使用 "mut" 關鍵字而不使用 "let" 在第 2 個 money 變數上,否則會導致型別不同,不能改值。

數字可以參考千分位符號的概念,加上 '_' 字元好方便閱讀。此外,如果是沒使用到的變數,可以在變數名稱前加上 "_",以避免 compiler warning。

let money = "100000";  // from some function
let money =  100_000;
let _x = 1;

2019年6月17日 星期一

Rust 探索之旅 - Cargo

Cargo 除了是編譯工具,也是 Rust 的套件管理工具。

在 Rust 語言裡,我們會稱呼模組為 crate,如果需要什麼模組,我們可以來 https://crates.io/ 找尋是否有現成的可以用。

之前文章中提到,我們可以用 cargo 來初始專案,專案資料夾內會有一個 Cargo.toml 的設定檔,其中 [dependencies] 區塊就是用來宣告我們使用到的其他模組。

我們只要簡單的加入模組名稱及版本號,執行 cargo build 時,便會去下載模組。
[dependencies]
rand = "0.3.14"
另外,第一次執行  cargo build 時,會產生一個 Cargo.lock 檔案,裡面會記錄相關模組的版本,這個機制可以確保別人在編譯專案時,會跟開發者當初使用的一致。

2019年6月12日 星期三

Rust 探索之旅 - Can't work on Windows 7

執行 cargo -V or rustc -V 時,程式就沒反應,不論是用工作管理員或是 taskkill 指令都無法停止相關程式,目前只知網路上的看法是跟防毒軟體有關。

遇到這種問題時,連關機都無法正常,只能強制斷電,已經斷電了 3 次。

我個人認為是跟 wrapper 的檔案有關﹝user/.cargo/bin﹞,而不是真實的執行檔有問題﹝user/.rustup/toolchains/stable-x86_64-pc-windows-msvc/bin﹞,也許可以把真實的執行檔加入到 PATH,而不要使用 wrapper 的位置?

https://github.com/rust-lang/rustup.rs/issues/1075
https://github.com/rust-lang/cargo/issues/3673

2019/06/18 更新
砍掉 "user/.cargo" 裡的 cargo.exe and rustc.exe,直接使用 "user/.rustup" 的執行檔就正常了,記得把這個路徑加入到 PATH

Rust 探索之旅 - Hello World

早上坐公車時無聊,看了一下 Rust 教學文件,開始我的 Rust 探索之旅。

Rust 和 C 一樣,都是編譯型語言,透過編譯會得到一個單一執行檔,可以方便的發佈給其他人。不像 script language,對方要執行時,需要有相關的安裝環境﹝理論上的,因為有些語言有第三方工具可以 build executable binary ﹞。

Rust 目前已知特點
01. cargo new "helloworld",可以自動建立一個 "helloworld" 的專案,"helloworld" 資料夾裡面會有 git 結構、Cargo.toml 的專案描述檔以及一個建立好的 "src\main.rs" 程式碼檔案,副檔名 "rs" 表示這是一個 Rust 的原始碼檔案。
02. 同 Go 一樣,cargo run 可以直接執行,cargo build 則是編譯 binary。
03. Rust 使用 let 宣告變數,變數預設是 immutable,加上 mut﹝mutable﹞表示變數可以被改值。
04. Rust 使用 use 宣告來存取內建函數。
05. "println!" 是一個 macro 而不是函數。
06. StringUTF-8 的編碼。
07. & 表示傳進函數的是一個參考﹝reference﹞,也可以加上 mut 關鍵字。
08. 在 println 中,"{}" 表示一個佔位符,有幾個後面就要有幾個對應的變數。
09. read_line包含換行字元,因為我們是 String,可以用 num.trim( ) 來去除。
10. Rust 變數命名慣例是小寫駝峰式,例如:s_len。


use std::io;

fn main() {
    println!("Guess the number");
    println!("Please input your guess");

    let mut num = String::new();

    io::stdin().read_line(&mut num)
        .expect("Failed to read line");

    println!("Your guess: {}", num);

    let s_len = num.len();
    let bytes = num.into_bytes();
    println!("lenth = {}, bytes = {:?}", s_len, bytes);
}

C:\helloworld>cargo run
Guess the number
Please input your guess:
123
Your guess: 123

lenth = 5, bytes = [49, 50, 51, 13, 10]

C:\helloworld>