函數(shù)

2018-08-12 22:03 更新

函數(shù)

每個 Rust 程序至少包含一個函數(shù),也就是 main 函數(shù):

 fn main() {
 }

這可能是最簡單的函數(shù)聲明。正如我們前面所提到的,fn 表明“這是一個函數(shù)”,緊隨其后的是函數(shù)名字,一些括號,因為這個函數(shù)沒有參數(shù),然后一些花括號來表示函數(shù)體。這里有一個函數(shù)名稱為 foo 的函數(shù):

 fn foo() {
 }

那么,函數(shù)的帶參數(shù)是什么樣的呢?如下是打印一個數(shù)字的函數(shù):

 fn print_number(x: i32) {
    println!("x is:{}", x);
 }

如下是使用 print_number 函數(shù)的完整程序:

 fn main() {
    print_number(5);
 }

 fn print_number(x: i32) {
    println!("x is: {}", x);
 }

正如你所看到的,函數(shù)參數(shù)的編寫方式和 let 聲明很相似:您可以在冒號之后添加參數(shù)的類型。

如下是一個將兩個數(shù)加起來然后打印的完整程序:

fn main() {
    print_sum(5, 6);
}

fn print_sum(x: i32, y: i32) {
    println!("sum is: {}", x + y);
}

當你調用和聲明函數(shù)的時候,都是利用逗號分隔參數(shù)。

不像 let,你必須聲明函數(shù)參數(shù)的類型。如下的代碼并不會正常工作:

 fn print_sum(x, y) {
    println!("sum is: {}", x + y);
 }

將會輸出如下錯誤:

 expected one of `!`, `:`, or `@`, found `)`
fn print_number(x, y) {

這是一個深思熟慮的設計決策。盡管可以通過推理判斷出數(shù)據(jù)類型,比如 Haskell 語言就擁有這種特性,但通常推薦顯式聲明是一種最佳實踐。我們贊成強制函數(shù)聲明類型推這種方式,盡管能夠在函數(shù)內部能夠推斷出變量的類型,這種方式對于指出全文推斷和支持推斷來說都是是個不錯方式。

返回值是什么樣的呢?如下這個函數(shù)給整型參數(shù)進行加 1 操作:

fn add_one(x: i32) -> i32 {
    x + 1
}

Rust 函數(shù)僅僅只能返回一個值,并且在一個“箭頭”符號之后聲明返回值類型,該“箭頭”由是一個破折號(-)和大于符號(>)組成。函數(shù)的最后一行決定它的返回值是什么。您會注意到這里函數(shù)最后一行缺少分號。如果我們給它加上分號:

fn add_one(x: i32) -> i32 {
    x + 1;
}

我們會得到一個錯誤:

error: not all control paths return a value
fn add_one(x: i32) -> i32 {
     x + 1;
}

help: consider removing this semicolon:
     x + 1;
          ^

這揭示了 Rust 兩個有趣的事情:它是一門基于表達式的語言,并且分號與其他基于花括號和分號的語言中的分號是不同的。這兩件事是相關的。

表達式 VS 語句

Rust 是一門主要基于表達式的語言。它只有兩種類型的語句,其他的都是表達式。

那么有什么區(qū)別?表達式返回一個值,和但語句并不會。這也就是為什么我們以“并不是所有的控制路徑返回一個值”結尾,在這里語句 x + 1;不返回一個值。Rust 中有兩種類型的語句:“聲明語句”和“表達式語句”。其他的都是一個表達式。讓我們先談談聲明語句。

在一些語言中,變量綁定可以寫成表達式的形式,而不僅僅是語句。像 Ruby:

 x = y = 5

然而在 Rust 中使用 let 引起的綁定不是一個表達式。以下將會產生編譯時錯誤:

 let x = (let y = 5); // expected identifier, found keyword 'let'

編譯器告訴我們在這里它希望看到一個表達式的開始,而 let 只能聲明一個語句,而不是一個表達式。

注意,給一個已經(jīng)綁定變量(如 y = 5)賦值仍然是一個表達式,雖然它的值不是特別有用。不像其他語言,賦值語句的值為被賦值的值(例如前面例子中的 5),在 Rust 中賦值語句的值是一個空元組 ():

 let mut y = 5;
 let x = (y = 6); //x has the value '()', not '6'

Rust 中的第二種語句是表達式語句。它的目的是把任何表達式變成一個語句。實際上,Rust 的語法希望在語句之后還是語句。這意味著您可以使用分號來將表達式分隔開。這意味著 Rust 看起來很像大多數(shù)其他語言,需要你在每一行的末尾使用分號,而且你看到的所有 Rust 代碼幾乎都是以分號作為行結束符。

是什么例外讓我們說“幾乎”?其實你已經(jīng)看到它,如下這段代碼中:

fn add_one(x: i32) -> i32 {
    x + 1
}

函數(shù)聲明返回一個 i32 類型的值,但如果語句末尾是分號,它將返回 ()。Rust 意識到這可能不是我們想要的,于是正如在前面我們看到錯誤中建議刪除分號。

提前返回

如果出現(xiàn)提前返回呢?Rust 中的確有一個 return 關鍵字:

fn foo(x: i32) -> i32 {
    return x;

    // we never run this code!
    x + 1
}

利用 return 關鍵字仍然會將函數(shù)的最后一行返回,但是這被認為是糟糕的編程風格:

fn foo(x: i32) -> i32 {
    return x + 1;
}

如果你以往沒有使用過基于表達式的語言,那么前面沒有使用 return 的定義可能看起來有些奇怪,但是使用的時間長了就會變得很自然了。

發(fā)散函數(shù)

Rust 對于 ‘發(fā)散函數(shù)’有一些特殊的語法,該函數(shù)不返回任何值:

fn diverges() -> ! {
    panic!("This function never returns!");
}

panic! 是一個宏,和 println!() 類似,我們已經(jīng)看見過。與 println!() 不同的是 panic!() 會導致當前執(zhí)行線程的崩潰并給出特定的消息。

因為這個函數(shù)會導致崩潰,它永遠不會返回,所以它有 “!” 類型,讀“發(fā)散”。發(fā)散函數(shù)可以作為任何類型:

let x: i32 = diverges();
let x: String = diverges();
以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號