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学习