函数及其返回值

使用fn声明并定义函数


fn用于声明定义一个函数,声明定义一个函数的大致格式如下:

1
2
3
4
5
6
7
8
9
// Rust函数名及变量名使用`snake case`规范风格
// 注意参数二前面加了`mut`,此时便可获取arg_name2的修改权
fn function_name(/* arg_name1: type, mut arg_name2: type */) -> return_type {
// code
}

// 无返回值函数
fn func_name(/* arg... */) /* -> () */ {
}

fn可以在全局和任意代码块内使用:

1
2
3
4
5
6
7
8
9
fn main() {
// 注意,`test1()`和`test2()`只能在其声明的作用域内(包括语句前和语句后)使用
fn test1() {
test2(); // 可以在声明前使用
fn test2() {
}
}
// test2(); // 此句无法编译,因为test2()不在作用域内
}

需要注意的是,Rust中不允许声明重名的函数,即使参数不一样也不行。

刚刚提到了,可以在函数参数前加mut获取修改权,回忆一下,讲解隐藏(shadowing)的最后,我说明了无法修改不可变变量。接下来,我们尝试通过函数修改不可变变量:

1
2
3
4
5
6
7
8
9
10
fn modify(mut n: i32) {
n = 12;
println!("{}", n);
}

fn main() {
let nt = 1;
modify(nt);
println!("{}", nt);
}

cargo run,输出:

1
2
12
1

很明显,虽然成功修改了n,但是nt却没有发生修改,原因涉及到超前知识,因此这里就简单解释一下:在向modify()函数传参过程中,并没有将nt移动到函数中,而是将其复制(copy)到函数中作为n的值,因此修改的是n的值即nt的拷贝,而不是nt本身。

需要提醒的是,Rust传参时分两种情况:复制(copy)和移动(move)。关于

函数返回值


在Rust的函数中,有两种返回值的方式,一种是使用return

1
2
3
fn func1(num: i32) -> i32 {
return num + 2;
}

另一种则是直接在函数代码块最后一行放置表达式

1
2
3
4
5
fn func2(num: i32) -> i32 {
if num == 0 { return 0; }
let k = num * num + 3;
k / num
}

num != 0时,func2()将返回k / num,整数除法结果不会保留小数部分,也不会四舍五入。

ifloop的返回值

在Rust中,ifloop{}也有返回值,下面给出了一些用法示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fn main() {
let fg = true;
let mut i = if fg {
3
} else {
4
}; // 不要忘了`let`语句后加分号

let mut res = 1;
let l = loop {
// `loop`返回值的方法比较特殊,要使用`break`
if i == 0 { break res; }
i -= 1; res += res;
}; // 别忘了语句后加分号

let b = {
res *= i;
res - 3
}; // 再三提醒别忘记语句分号
}

和函数不一样,ifloop{}不能使用return返回值。

ifloop{}可以看作特殊的函数。因为返回值可以是表达式,所以可以这么写Rust代码:

1
2
3
4
5
6
7
8
9
fn func(fg bool) -> i32 {
if fg {
{
3
}
} else {
func(!fg) + 4
}
}

但是{}不可乱用,比如你不能这么写:

1
2
3
fn func() -> i32 {
{ 3 } + 3
}

因为{}主要用于划分作用域,Rust编译器不允许这么写(也不建议这么写),如有需要,可以:

1
2
3
4
5
6
7
8
fn func1() -> i32 {
({ 3 }) + ({ 4 })
}

// 或使用return告诉编译器这里的{}不用于划分作用域
fn func2() -> i32 {
return { 3 } + 5;
}