<<Rust程序設計語言>>個人版(3.3: 函數/3.4: 註釋/3.5: 控制流)

常見程序設計概念

函數

函數在rust中無處不在, 對於rust程序來講, main函數是許多程序的入口, 之前我們知道, 建立一個函數的關鍵字是 fn

rust使用下劃線命名法來命名, 這個之前也有提到過

我們來看下面的程序

fn main() {
    println!("Hello, world!");

    another_function();
}

fn another_function() {
    println!("hello another_function!");
}

rust中, 函數的範圍由 {} 指定, 也就是說, 這段代碼中有兩個函數, main 和 another_function, 程序的入口是 main, 在 main 中調用了 another_function, 當我們從上往下看的時候, 會發現在 another_function 未定義之前就調用了 another_function, 這樣也是可以的, rust 並不會報錯, 只要你存在, 不管在哪裏都可以.

在函數中邏輯是從上到下執行的, 因此會先打印 Hello, world!, 在執行 another_function, another_function 中會打印 hello another_function!

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
    Finished dev [unoptimized + debuginfo] target(s) in 1.00s
     Running `target/debug/fun`
Hello, world!
hello another_function!

函數的參數

很多時候, 函數需要根據參數來進行操作, rust需要在建立函數時定義參數的名稱與類型, 寫在函數名後的 ()

fn main() {
    another_function(5);
}

fn another_function(x: i32) {
    println!("x = {}", x);
}
➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
    Finished dev [unoptimized + debuginfo] target(s) in 0.40s
     Running `target/debug/fun`
x = 5

對於函數 another_function 來講, 我們設置了一個參數 x, 他是 i32 類型, 需要注意的是, 我們必須對每個參數都說明類型, 不然會導致編譯錯誤

當某個函數的需要多個參數時, 我們這樣寫

fn main() {
    another_function(1, 2);
}

fn another_function(x: i32, y: i32) {
    println!("x = {}", x);
    println!("y is {}", y);
}

函數的內容包含了陳述式和表達式

函數的內容是由一系列的陳述式(statements)和在後面的可選的表達式(expression)組成的, rust是基於表達式的語言, 下面我們來介紹這兩個的區別

陳述式是一系列動作的指令, 陳述式不回傳任何數據

表達式則是通過邏輯處理來產生結果, 也就是說表達式返回數據

比如代碼

fn main() {
    let a = 1;
}

這裏的mian函數本身是一個陳述式, 因爲他不返回任何數據

這裏的 let a = 1; 也是一個陳述式, 因爲他不返回任何數據, 因爲他不返回任何數據, 所以你不能接受他的返回數據, 例如

fn main() {
    let b = (let a = 1);
}

這就會導致編譯出錯, 因爲let a = 1是一個陳述式, 他不返回任何數據, 但是你試圖使用返回值來作爲 b 的值

而表達式則會給出結果, 比如 1+1 會返回2, 表達式可以是陳述式的一部分, 比如 let a = 1中的1就是一個表達式, 他返回了一個1, 同時我們使用{}產生的作用域也是一個表達式, 例如

fn main() {
    let x = 5;

    let y = {
        let x = 3;
        x + 1
    };

    println!("y = {}", y);
}

其中, 表達式

    let y = {
        let x = 3;
        x + 1
    };

會返回4, 此時使用 y 接收就能獲得 4, 這是因爲 x + 1後面並沒有帶上 ;, 如果你加上了 ; 則不會返回結果, 這是必須要記住的

函數返回值

函數的返回值定義是->, 寫在接收參數的()後, 同樣的要定義他的類型. 但是無需命名

fn five() -> i32 {
    5
}

fn main() {
    let x = five();

    println!("x = {}", x);
}

需要注意, five 函數裏直接寫了5, 但是沒有跟;, 所以他會返回5, 同時 five 定義了返回值是一個, 類型是 i32

同時 main 中定義了 x 來接受 five 的返回值, 所以 x 爲 5, 同時函數 five 爲表達式

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
    Finished dev [unoptimized + debuginfo] target(s) in 0.57s
     Running `target/debug/fun`
x = 5

當我們爲 5 加上 ;, five 就變成了 陳述式, 此時在編譯時就會報錯

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
error[E0308]: mismatched types
 --> src/main.rs:1:14
  |
1 | fn five() -> i32 {
  |    ----      ^^^ expected `i32`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
2 |     5;
  |      - help: consider removing this semicolon

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `fun`.

To learn more, run the command again with --verbose.

當這個函數有多個返回值, 則使用()包裹

fn five() -> (i32, i32) {
    (5, 6)
}

fn main() {
    let x = five();

    println!("x0 = {}, x1 = {}", x.0, x.1);
}
➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
    Finished dev [unoptimized + debuginfo] target(s) in 0.25s
     Running `target/debug/fun`
x0 = 5, x1 = 6

註釋

一個好的程序, 註釋並不可少. 多寫註釋(comments)對自己和他人都有好處

編譯器在編譯時會將註釋部分去除, 所以不用擔心會增加編譯文件的大小

rust的註釋使用 // , 一般會在 // 後加上一個空格

// 這裏是註釋

需要注意的是, //標識改行爲註釋, 因此 // 後一直到本行結束之間的所有東西都被編譯器認爲是註釋

當需要多行註釋時, 爲每一行都加上 // 即可

// 註釋1
// 註釋2

註釋也可以加在某一行代碼的結束

fn main() {
    let x = 3; // 註釋
}

控制流程

在程序中, 一個流程通常有多個分支, 我們需要在某個時候根據某個條件來決定怎麼做

if表達式

if表達式根據條件的不同執行不同的代碼. 當滿足條件時就執行這段代碼, 不滿足時就不執行

fn main(){
    let a = 5;
    if a < 5{
        println!("<5")
    }else{
        println!(">=5")
    }
}

這裏我們判斷a是否小於5, 根據不同的情況來打印不同的結果

需要注意的是, 其中某個分支的邏輯必須使用 {} 包裹

並且, a<5 返回的是一個bool, rust中if表達式只能使用bool來進行判斷, 加入我們將代碼修改成

fn main(){
    let a = 5;
    if a{
        println!("<5")
    }else{
        println!(">=5")
    }
}

則會導致編譯失敗, 這是因爲a是int 而不是bool

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
error[E0308]: mismatched types
 --> src/main.rs:3:8
  |
3 |     if a{
  |        ^ expected `bool`, found integer

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `fun`.

To learn more, run the command again with --verbose.

rust並不會將其他非bool值自動轉化成bool

使用 else if 處理多個情況

使用多個else ififelse一起使用來處理多個分支

fn main(){
    let a = 5;
    if a == 4{
        println!("4")
    }else if a == 5{
        println!("5")
    }else if a == 5{
        println!("51")
    }else{
        println!("not")
    }
}

需要注意的是, 在這個代碼塊中, 判斷只會成功一次, 也就是說, 在這個多重判斷中, 即使a可以爲true兩次, 也只會執行一次, 也就是隻會打印5 而不是51

當你有多個分支時, 不推薦使用大量的 else if, 這樣會導致代碼看起來不友好, 後面會推薦使用match

在let中使用if

fn main(){
    let t = true;
    let s = if t{
        5
    }else{
        6
    };
    println!("{}", s)
}

這裏就是, 我們在賦值的時候, 根據t的不同來返回不同的值給s, 這裏注意, 寫一個數字本身就是一個表達式, 也就是說如果t爲true, s就爲5, 其他爲6

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
    Finished dev [unoptimized + debuginfo] target(s) in 0.36s
     Running `target/debug/fun`
5

這種寫法有一個限制, 就是if的返回值必須是同一類型, s不可能有可能是int有可能是str, 例如

fn main(){
    let t = true;
    let s = if t{
        5
    }else{
        "6"
    };
    println!("{}", s)
}

這種就會報錯

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
error[E0308]: `if` and `else` have incompatible types
 --> src/main.rs:6:9
  |
3 |       let s = if t{
  |  _____________-
4 | |         5
  | |         - expected because of this
5 | |     }else{
6 | |         "6"
  | |         ^^^ expected integer, found `&str`
7 | |     };
  | |_____- `if` and `else` have incompatible types

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
error: could not compile `fun`.

To learn more, run the command again with --verbose.

因爲rust在編譯時就必須準確的知道s的類型, 但是s可能會有兩種類型, 這樣會導致代碼有可能出現問題

循環

循環可以重複的執行某段代碼

rust有三種循環: loop/while/for

loop

loop關鍵字會讓rust一直重複執行代碼一直到明確的要求結束

看以下代碼

fn main(){
    loop{
        println!("a")
    }
}

此程序是死循環, 不停的打印字符串a, 因爲沒有使用關鍵字break, 所以不會終止循環

從循環返回

fn main(){
    let mut c = 0;
    let r = loop{
        c += 1;
        if c == 10{
            break c*2;
        }
    };
    println!("{}", r)
}

我們可以定義一個變量來接受某個循環的返回值

while循環

我們使用循環時, 多會使用一個大的嵌套, 比如loop{}, 其實rust中的while可以在特定的場景下減少代碼, 比如

fn main(){
    let mut c = 4;
    while c != 0{
        c -= 1;
        println!("{}", c)
    }
    println!("OK")
}

while可以與一個判斷條件一起使用, 比如這裏就是判斷c!=0, 如果c!=0false時則退出循環. 而在循環內則對c進行-1, 代碼邏輯等同於

fn main(){
    let mut c = 4;
    loop {
        c -= 1;
        println!("{}", c);
        if c == 0{
            break;
        };
    }
    println!("OK")
}

for循環

使用遍歷來描述for可能更爲準確, for並不是死循環, 而是遍歷完成就結束

fn main(){
    let c = [1, 2, 3, 4];
    for i in c.iter(){
        println!("{}", i)
    }
}

這裏我們遍歷c這個數組, 注意c.iter()可以生成一個range, 每次拋出c的一個元素, 使用for可以防止索引超出範圍

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
    Finished dev [unoptimized + debuginfo] target(s) in 0.42s
     Running `target/debug/fun`
1
2
3
4

再比如

fn main(){
    let c = [1, 2, 3, 4];
    for i in c.iter().rev(){
        println!("{}", i)
    }
}

rev方法可以翻轉一個range, 因此會輸出4-1

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32s
     Running `target/debug/fun`
4
3
2
1

練習

編寫程序實現攝氏度與華氏度之間的轉換

use std::io; // 引入標準庫

fn main(){
    loop {
        println!("選擇轉換模式: 
        1: 攝氏度轉華氏度
        2: 華氏度轉攝氏度
        其他任意鍵: 退出
        >>>");
        let mut model = String::new();
        io::stdin().read_line(&mut model).expect("讀取失敗"); // 如果獲取錯誤打印警告 
        if model.trim() == "1"{
            println!("輸入你要轉換的攝氏度>>>");
            let mut c = String::new();
            io::stdin().read_line(&mut c).expect("讀取失敗"); // 如果獲取錯誤打印警告 
            let c: f32 = match c.trim().parse() {
                Ok(num) => num,
                Err(_) => {
                    println!("輸入了一個無法解析的字符串");
                    continue;
                },
            };
            let f = transformation_centigrade(c);
            println!("攝氏度:{}對應的華氏度是:{}", c, f);
        }
        else if model.trim() == "2"{
            println!("輸入你要轉換的華氏度>>>");
            let mut f = String::new();
            io::stdin().read_line(&mut f).expect("讀取失敗"); // 如果獲取錯誤打印警告 
            let f: f32 = match f.trim().parse() {
                Ok(num) => num,
                Err(_) => {
                    println!("輸入了一個無法解析的字符串");
                    continue;
                },
            };
            let c = transformation_fahrenheit_degree(f);
            println!("華氏度:{}對應的攝氏度是:{}", f, c);
        }
        else{
            println!("exit.");
            break;
        }
    }
}

// 華氏度2攝氏度
fn transformation_fahrenheit_degree(f: f32) -> f32{
    return (f-32.0)/1.8;
}

// 攝氏度2華氏度
fn transformation_centigrade(c: f32) -> f32{
    return 32.0+c*1.8;
}

運行

➜  fun git:(master) ✗ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/fun`
選擇轉換模式: 
        1: 攝氏度轉華氏度
        2: 華氏度轉攝氏度
        其他任意鍵: 退出
        >>>
1
輸入你要轉換的攝氏度>>>
222
攝氏度:222對應的華氏度是:431.59998
選擇轉換模式: 
        1: 攝氏度轉華氏度
        2: 華氏度轉攝氏度
        其他任意鍵: 退出
        >>>
2
輸入你要轉換的華氏度>>>
4444
華氏度:4444對應的攝氏度是:2451.111
選擇轉換模式: 
        1: 攝氏度轉華氏度
        2: 華氏度轉攝氏度
        其他任意鍵: 退出
        >>>
q
exit.

生成 n 階斐波那契數列

use std::io; // 引入標準庫

fn main(){
    loop {
        println!("輸入期望的n>>>");
        let mut n = String::new();
        io::stdin().read_line(&mut n).expect("讀取失敗");
        let n: usize = match n.trim().parse() {
            Ok(num) => num,
            Err(_) => {
                println!("輸入了一個無法解析的字符串");
                continue;
            },
        };
        let mut c: usize = 0;
        let mut s0 = 0;
        let mut s1 = 1;
        println!("結果: ");
        loop{
            if c == n{
                break;
            };
            let mut f = 0;
            if c == 0{
                f = 0;
            }else{
                f = s0+s1;
            }
            s0 = s1;
            s1 = f;
            c+=1;
            print!("{} ", f)
        }
        println!("")
    }
}

運行

➜  fun git:(master) ✗ cargo run
   Compiling fun v0.1.0 (/Users/Work/Code/Rust/student/fun)
warning: value assigned to `f` is never read
  --> src/main.rs:23:21
   |
23 |             let mut f = 0;
   |                     ^
   |
   = note: `#[warn(unused_assignments)]` on by default
   = help: maybe it is overwritten before being read?

    Finished dev [unoptimized + debuginfo] target(s) in 0.48s
     Running `target/debug/fun`
輸入期望的n>>>
3
結果: 
0 1 1 
輸入期望的n>>>
1
結果: 
0 

打印歌曲 “The Twelve Days of Christmas” 的歌詞, 使用循環

fn main(){
    let days = ["first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth"];
    let gift = ["a partridge in a pear tree", "two turtle doves", "three French hens", "four calling birds", "five golden rings", "six geese a-laying", "seven swans a-swimming", "eight maids a-milking", "nine ladies dancing", "ten lords a-leaping", "eleven pipers piping", "twelve drummers drumming"];
    let mut n = 0;
    println!("The Twelve days of Christmas");
    for d in days.iter(){
        print!("On the {} day of Christmas, my true love sent to me: ", d);
        let mut gi = n;
        loop{
            if gi != 0{
                print!("{}, ", gift[gi]);
                gi -= 1;
            }else if n == 0{
                print!("{} \n", gift[gi]);
                break;
            }else{
                print!("and {} \n", gift[gi]);
                break;
            }
        };
        n += 1
    };
}

運行

The Twelve days of Christmas
On the first day of Christmas, my true love sent to me: a partridge in a pear tree 
On the second day of Christmas, my true love sent to me: two turtle doves, and a partridge in a pear tree 
On the third day of Christmas, my true love sent to me: three French hens, two turtle doves, and a partridge in a pear tree 
On the fourth day of Christmas, my true love sent to me: four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the fifth day of Christmas, my true love sent to me: five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the sixth day of Christmas, my true love sent to me: six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the seventh day of Christmas, my true love sent to me: seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the eighth day of Christmas, my true love sent to me: eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the ninth day of Christmas, my true love sent to me: nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the tenth day of Christmas, my true love sent to me: ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the eleventh day of Christmas, my true love sent to me: eleven pipers piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
On the twelfth day of Christmas, my true love sent to me: twelve drummers drumming, eleven pipers piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five golden rings, four calling birds, three French hens, two turtle doves, and a partridge in a pear tree 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章