mian 函數表明程序的入口,對於二進制可執行文件來說,main 函數必不可少,對於庫函數來說,main 函數沒有必要。數組
Rust 中函數經過 fn 定義。代碼示例安全
pub fn fizz_buzz(num: i32) -> String {
if num % 15 == 0 {
return "fizzbuzz".to_string();
} else if num % 3 == 0 {
return "fizz".to_string();
} else if num % 5 == 0 {
return "buzz".to_string();
} else {
return num.to_string();
}
}
fn main () {
assert_eq!(fizz_buzz(15), "fizzbuzz".to_string());
assert_eq!(fizz_buzz(3), "fizzbuzz".to_string());
assert_eq!(fizz_buzz(5), "buzz".to_string());
assert_eq!(fizz_buzz(13), "13".to_string());
}
複製代碼
函數解析: fn 關鍵字定義了 fizz_buzz 函數,其函數簽名pub fn fizz_buzz(num: i32) -> String
反應函數的類型約定: 傳入 i32 類型,返回 String 類型。Rust 編譯器會嚴格遵照此類型的契約,若是傳入或返回類型不對,則編譯時報錯。markdown
Rust 語言的做用域是靜態做用域,即詞法做用域(Lexocal Scope),由一對花括號來開闢,其做用域在詞法分析階段就已經肯定了,不會動態改變。示例以下閉包
fn main () {
let v = "hello world!";
assert_eq!(v, "hello world!");
let v = "hello Rust!";
assert_eq!(v, "hello Rust!");
{
let v = "Hello World!"
assert_eq!(v, "Hello World!");
}
assert_eq!(v, "hello Rust!");
}
複製代碼
首先聲明 v 變量,賦值爲 hello world!, 此時斷言驗證其值,再次經過 let 聲明變量綁定 v 賦值爲 hello Rust! 這種連續定義同名變量的作法叫作變量遮蔽(Variable Shadow),最終變量 v 的值是由第二個變量定義所決定的。函數
在 Rust 中,函數爲一等公民,意味着函數自身就能夠做爲函數的參數和返回值使用。示例:學習
fn sum(a: i32, b: i32) -> i32 {
a + b
}
fn product(a: i32, b: i32) -> i32 {
a * b
}
fn main () {
let a = 2;
let b = 3;
assert_eq!(math(sum, a, b), 5);
assert_eq!(math(product, a, b), 6);
}
複製代碼
在 main 函數中,調用了 math 函數兩次,分別傳入了 sum 和 product 做爲參數。而 sum 和 product 分別是用於求和求積的兩個函數,他們的類型分別是fn (i32, i32) -> i32
,因此能夠做爲參數傳給 math 函數。這裏直接使用函數的名字來做爲函數指針。spa
fn is_true -> bool {true}
fn true_maker() -> fn() -> bool{is_true}
fn main() {
assert_eq!(true_maker()(), true);
}
複製代碼
定義函數中 is_true
, 返回true
,還定義了函數 true_maker
,返回 fn() -> bool
類型,器函數體內直接將 is_true 函數指針返回,此處函數名稱做爲函數指針,若是加上括號,表明調用此函數。 在 main 函數的斷言中, true_maker()()
調用至關於(true_maker())()
首先調用 true_maker()
,會返回 is_true
函數指針,而後再調用 is_true()
函數,最終獲得 true
。指針
Rust 編譯器也能夠像 C++ 或者 D 語言同樣,擁有編譯時函數執行(Compile-time Function Execution, CTFE)能力。示例爲 const fn
code
// #![feature(const_fn)]
const fn init_len() -> usize {
return 5;
}
fn main () {
let arr = [0, init_len()];
}
複製代碼
代碼中,使用了 const fn
來定義函數 init_len
,該函數返回一個固定值 5 。而且在 main 函數中,經過 [0; N]
的方式初始化初始值爲 0、長度爲 N 的數組,其中 N 是調用函數 init_len
來求得的。 Rust 固定長度數組必須在編譯期就知道長度,不然就會編譯出錯,因此 init_len
必須在編譯期求值。這就是 CTFE 的能力。 使用 const fn
定義的函數,必須是可肯定值,不能存在歧義。與 fn 定義函數的區別在於,const fn
能夠強制編譯器在編譯期執行函數。其中關鍵字 const 通常用於定義全局變量。orm
除了 const fn
以外,官方還在實現 const generics
特性。支持 const generics
特性,將能夠實現相似 impl <T, const N: unsize> Foo for[T;N] {…}
的代碼,能夠爲全部長度的數組實現 trait Foo
。可以使得使用數組的體驗獲得很大提高。
Rust 中的 CTFE 是有 miri 來執行的。miri 是一個 MIR 解釋器,目前已經被集成到了 Rust 編譯器 rustc 中。Rust 編譯器目前能夠支持的常量表達式有: 字面量、元祖、數組、字段結構體、枚舉,只包含但行代碼的塊表達式、範圍等。Rust 想要擁有完善的 CTFE 支持,還須要不少工做要作。
閉包也稱匿名函數,閉包有一下幾個特色: * 能夠像函數同樣被調用。 * 能夠駁貨上下文環境中的自由變量。 * 能夠自動推斷輸入和返回的類型。
fn main () {
let out = 42;
// fn add (i: i32, j: i32) -> i32 {i + j + out}
fn add (i: i32, j: i32) -> i32 { i + j}
let closure_annotated = |i: i32, j: i32| -> i32 {i + j + out}
let closure_inferred = |i, j| i + j + out;
let i = 1;
let j = 2;
assert_eq!(3, add(i, j));
assert_eq!(45, closure_annotated(i, j));
assert_eq!(45, closure_inferred(i, j));
}
複製代碼
上述代碼中,main 函數中定義了另一個函數 add, 以及兩個閉包 closure_annotated 和 closure_inferred。 閉包調用和函數調用很是想,可是閉包合函數有一個重要的區別,就是閉包能夠捕獲外部變量,而函數不能。這與 JavaScript 有很大的差異。JavaScript 函數自己就能夠獲取外部變量,只是獲取到外部變量不必定就是閉包。 閉包也能夠做爲函數參數和返回值,可是用起來略有區別。
fn closure_math<F: Fn() -> i32>(op: F) -> i32 {
op()
}
fn main () {
let a = 2;
let b = 3;
assert_eq!(closure_math(|| a + b), 5);
assert_eq!(closure_math(|| a * b), 6);
}
複製代碼
上述代碼中,定義了函數 closure_math,其參數是一個泛型 F, 而且該泛型 Fn() -> i32
trait 的限定,表明該函數只容許實現 Fn() -> i32
trait 的類型做爲參數。 Rust 中的閉包實際上就是由一個匿名結構體和 trait 來組合實現的。因此,在 main 函數調用 math 函數時,分別傳入 || a + b
和 || a * b
均可以實現 Fn() -> i32
。 在 math 函數內部,直接調用傳入閉包。
fn tow_times_impl() -> impl Fn(i32) -> i32 {
let i = 2;
move |j| j * i
}
fn main () {
let result = tow_times_impl();
assert_eq!(result(2), 4);
}
複製代碼
在上述代碼中,使用了 impl Fn(i32) -> i32
做爲函數的返回值,他表示實現 Fn(i32) -> i32
的類型,在函數定義是並不知道具體的返回類型,可是在函數調用時,編譯器會推斷出來。這個過程是零成本抽象的,一切都發生在編譯期。
須要注意的是,two_times_impl
中最後返回閉包時使用了 move 關鍵字,這是由於通常狀況下,閉包默認會按引用捕獲變量,若是將閉包返回,則引用也會跟着返回,可是在整個函數調用完畢後,函數內的本地變量 i 就會被銷燬,那麼隨着閉包返回的變量 i 的引用,也就成了懸垂指針。 Rust 是注重內存安全的語言,若是不適用 move 關鍵字,編譯器會報錯。 使用 move 關鍵字,將捕獲變量 i 的全部權轉移到閉包中,就不會按引用進行捕獲變量,閉包纔可安全返回。
#Rust學習