Rust 程式設計教學:函式 (Function)

PUBLISHED ON AUG 23, 2017 — PROGRAMMING

在前面的內容中,我們將大部分的程式碼寫在主函式中。隨著程式規模上升,這種方式漸漸顯得不足:(1) 對於相同的步驟撰寫重覆的程式碼,(2) 主程式變得冗長,(3) 無法將程式碼分離再利用。函式 (function) 是程式碼再利用的基礎,透過函式將程式碼分離後,可再將函式整理成函式庫 (library) 分享給其他程式。物件導向程式設計 (object-oriented programming) 的方法 (method),也是建立在函式之上。

使用函式

Rust 預先寫好許多的函式,供程式設計者使用,在先前的例子中,我們已經使用過一些函式和物件。以下是一個例子:

在本例中,我們呼叫標準函式庫的 f64 模組,接著,呼叫 sqrt 函式。有時候,函式會和物件結合,在本書先前介紹容器時,就介紹了一些 Rust 內建的容器物件;和物件相連的函式,也稱為方法 (method)。

撰寫函式

撰寫函式或方法使用 fn 這個關鍵字。我們來看一個簡單的實例:

在本例中,hello 函式相當單純,每次呼叫時都會在終端機中印出 Hello, World 字串。由此可以看出,將程式碼寫入函式後,可以重覆呼叫。

在上例中,hello 函式的行為是固定的。函式可透過參數 (parameter) 來改變函式的行為。如下例:

在本例中,我們輸入不同的名字,hello 函式會印出不同的字串。雖然 Rust 可自動推斷型別,在撰寫 Rust 函式時,必需明確指定參數的型別,這是 Rust 設計上的考量。

除了可以傳入參數,函式也可以有回傳值 (returning value),如下例:

在本例中,我們依據所到的字元來回傳不同的值,若字元為空,回傳一個空字串,若字元不為空,則將首字母轉為大寫後和其他字元相接後回傳。要注意的是,回傳值後不加上分號 (;),在 Rust 中,沒加分號的是表達式 (expression),而有加分號的是敘述 (statement)。以下是一個錯誤的例子:

若改成表達式,則正確:

若不習慣這種語法,也可改為回傳敘述:

回傳表達式,是比較典型的 Rust 習慣語法。

Rust 的函式只能回傳單一值,而 Rust 發展出一些特殊容器來應對。例如前文提過的 Option 容器,可回傳 None 表空值或是以 Some 包裝的參考。另外一個例子是 Result 容器,可回傳值或是表示錯誤的 Err。

改變狀態的函式

若搭配參考 (reference),函式也可改變資料的狀態。如下例:

有 C 語言經驗的讀者,應該可以發現本例採用 C 風格的物件導向。由於 Rust 本身即支援物件導向,在實務上不建議這種寫法,這個範例僅是展示如何在函式中使用參考。

函式指標

函式可以視為一種型別,也可將函式視為值。如下例:

在這裡,也可以用 Rust 的型別推斷功能,將型別省略。在程式中能夠將函式視為值是函數式程式設計的基礎,詳見後續文章。

預設參數

Rust 的函式不支援預設參數,要用其他的方式來模擬這個特性。其中一個方式是利用 Default trait,範例如下:

在這個例子中,我們運用一點點物件導向的功能,將 Parameter 結構加人 default 函式,之後就可以呼叫此函式以得到預設值。Rust 大量運用 trait 實作物件導向及泛型相關的功能,想了解的讀者可在後續的章節看到更多範例。

遞迴

遞迴 (recursion) 是可以呼叫自己的函式,直到回到某個特定的基本條件為止。Fibonacci 數是一個典型的遞迴實例,程式碼範例如下:

初學者對遞迴程式感到困惑,而不知道如何追踪遞迴程式碼。遞迴函式就像是一般的函式般,是完成某個特定的行為的藍圖,而不是該行為本身。遞迴函式會在某個遞迴的步驟告訴電腦「在這裡我們要再呼叫一次這個方法,幫我完成這個步驟,然後將結果傳回來」。另外一個學習遞迴的方法,是重新將數學歸納法 (mathematical induction) 的教材看一次,這兩者間有異曲同工之妙。要寫好遞迴程式,關鍵在於設置正確的終止條件。以 Fibonacci 數來說,有兩個終止條件,即 F(0) = 1 和 F(1) = 1,其他的數字,最後都會呼叫到這兩個數字。

在程式設計中,遞迴是相當重要的概念,許多用控制流程的程式都可用遞迴重新改寫。使用遞迴實作程式,代碼往往更為簡潔。許多資料結構和演算法的內部,也大量使用遞迴,像是堆積 (heap)、樹 (tree)、圖 (graph)等。

TAGS: FUNCTION, RUST
comments powered by Disqus