到目前為止你應該見過一個函數,`main`函數:
~~~
fn main() {
}
~~~
這可能是最簡單的函數聲明。就像我們之前提到的,`fn`表示“這是一個函數”,后面跟著名字,一對括號因為這函數沒有參數,然后是一對大括號代表函數體。下面是一個叫`foo`的函數:
~~~
fn foo() {
}
~~~
那么有參數是什么樣的呢?下面這個函數打印一個數字:
~~~
fn print_number(x: i32) {
println!("x is: {}", x);
}
~~~
下面是一個使用了`print_number`函數的完整的程序:
~~~
fn main() {
print_number(5);
}
fn print_number(x: i32) {
println!("x is: {}", x);
}
~~~
如你所見,函數參數與`let`聲明非常相似:參數名加上冒號再加上參數類型。
下面是一個完整的程序,它將兩個數相加并打印結果:
~~~
fn main() {
print_sum(5, 6);
}
fn print_sum(x: i32, y: i32) {
println!("sum is: {}", x + y);
}
~~~
在調用函數和聲明函數時,你需要用逗號分隔多個參數。
與`let`不同,你_必須_為函數參數聲明類型。下面代碼將不能工作:
~~~
fn print_sum(x, y) {
println!("sum is: {}", x + y);
}
~~~
你會獲得如下錯誤:
~~~
expected one of `!`, `:`, or `@`, found `)`
fn print_number(x, y) {
~~~
這是一個有意為之的設計決定。即使像Haskell這樣的能夠全程序推斷的語言,也經常建議注明類型是一個最佳實踐。我們同意即使允許在在函數體中推斷也要強制函數聲明參數類型是一個全推斷與無推斷的最佳平衡。
如果我們要一個返回值呢?下面這個函數給一個整數加一:
~~~
fn add_one(x: i32) -> i32 {
x + 1
}
~~~
Rust函數確實返回一個值,并且你需要在一個“箭頭”后面聲明類型,它是一個破折號(`-`)后跟一個大于號(`>`)。
注意這里并沒有一個分號。如果你把它加上:
~~~
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主要是一個基于表達式的語言。只有兩種語句,其它的一切都是表達式。
然而這又有什么區別呢?表達式返回一個值,而語句不是。這就是為什么這里我們以“不是所有控制路徑都返回一個值”結束:`x + 1;`語句不返回一個值。Rust中有兩種類型的語句:“聲明語句”和“表達式語句”。其余的一切是表達式。讓我們先討論下聲明語句。
在一些語言中,變量綁定可以被寫成一個表達式,不僅僅是語句。例如Ruby:
~~~
x = y = 5
~~~
在Rust中,然而,使用`let`引入一個綁定并_不是_一個表達式。下面的代碼會產生一個編譯時錯誤:
~~~
let x = (let y = 5); // expected identifier, found keyword `let`
~~~
編譯器告訴我們這里它期望看到表達式的開頭,而`let`只能開始一個語句,不是一個表達式。
注意賦值一個已經綁定過的變量(例如,`y = 5`)仍是一個表達式,即使它的(返回)值并不是特別有用。不像其它語言中賦值語句返回它賦的值(例如,前面例子中的`5`),在Rust中賦值的值是一個空的元組`()`:
~~~
let mut y = 5;
let x = (y = 6); // x has the value `()`, not `6`
~~~
Rust中第二種語句是_表達式語句_。它的目的是把任何表達式變為語句。在實踐環境中,Rust語法期望語句后跟其它語句。這意味著你用分號來分隔各個表達式。這意味著Rust看起來很像大部分其它要求你使用分號來做每一行的結尾的語言,并且你會看到分號出現在幾乎每一行你看到的Rust代碼。
那么我們說“幾乎”的例外是神馬呢?你已經見過它了,在這寫代碼中:
~~~
fn add_one(x: i32) -> i32 {
x + 1
}
~~~
我們的函數聲稱它返回一個`i32`,不過帶有一個分號,它會返回一個`()`。Rust意識到這可能不是我們想要的,并在我們之前看到的錯誤中建議我們去掉分號。
## 提早返回(Early returns)
不過提早返回怎么破?Rust確實有這么一個關鍵字,`return`:
~~~
fn foo(x: i32) -> i32 {
return x;
// we never run this code!
x + 1
}
~~~
使用`return`作為函數的最后一行是可行的,不過被認為是一個糟糕的風格:
~~~
fn foo(x: i32) -> i32 {
return x + 1;
}
~~~
如果你之前沒有使用過基于表達式的語言那么前面的沒有`return`的定義可能看起來有點奇怪。不過它隨著時間的推移它會變得直觀。
## 發散函數(Diverging functions)
Rust有些特殊的語法叫“發散函數”,這些函數并不返回:
~~~
fn diverges() -> ! {
panic!("This function never returns!");
}
~~~
`panic!`是一個宏,類似我們已經見過的`println!()`。與`println!()`不同的是,`panic!()`導致當前的執行線程崩潰并返回指定的信息。
因為這個函數會崩潰,所以它不會返回,所以它擁有一個類型`!`,它代表“發散”。一個發散函數可以是任何類型:
~~~
let x: i32 = diverges();
let x: String = diverges();
~~~
我們并沒有很好的利用發散函數。因為它結合在別的Rust功能中。不過當你再看到`-> !`時,你要知道它是發散函數。
- 前言
- 1.介紹
- 2.準備
- 2.1.安裝Rust
- 2.2.Hello, world!
- 2.3.Hello, Cargo!
- 3.學習Rust
- 3.1.猜猜看
- 3.2.哲學家就餐問題
- 3.3.其它語言中的Rust
- 4.高效Rust
- 4.1.棧和堆
- 4.2.測試
- 4.3.條件編譯
- 4.4.文檔
- 4.5.迭代器
- 4.6.并發
- 4.7.錯誤處理
- 4.8.外部語言接口
- 4.9.Borrow 和 AsRef
- 4.10.發布途徑
- 5.語法和語義
- 5.1.變量綁定
- 5.2.函數
- 5.3.原生類型
- 5.4.注釋
- 5.5.If語句
- 5.6.for循環
- 5.7.while循環
- 5.8.所有權
- 5.9.引用和借用
- 5.10.生命周期
- 5.11.可變性
- 5.12.結構體
- 5.13.枚舉
- 5.14.匹配
- 5.15.模式
- 5.16.方法語法
- 5.17.Vectors
- 5.18.字符串
- 5.19.泛型
- 5.20.Traits
- 5.21.Drop
- 5.22.if let
- 5.23.trait對象
- 5.24.閉包
- 5.25.通用函數調用語法
- 5.26.包裝箱和模塊
- 5.27.`const`和`static`
- 5.28.屬性
- 5.29.`type`別名
- 5.30.類型轉換
- 5.31.關聯類型
- 5.32.不定長類型
- 5.33.運算符和重載
- 5.34.`Deref`強制多態
- 5.35.宏
- 5.36.裸指針
- 6.Rust開發版
- 6.1.編譯器插件
- 6.2.內聯匯編
- 6.3.不使用標準庫
- 6.4.固有功能
- 6.5.語言項
- 6.6.鏈接參數
- 6.7.基準測試
- 6.8.裝箱語法和模式
- 6.9.切片模式
- 6.10.關聯常量
- 7.詞匯表
- 8.學院派研究
- 勘誤