
函数:代码界的"打工人",给啥干啥,干完交活
你是不是已经写过一些 Rust 代码了?有没有发现,如果把所有代码都塞进 main 函数里,那画面简直不敢看——就像把所有家务活都堆在客厅里做,最后连下脚的地方都没有。
这时候就需要函数登场了。函数就像是你家的各个房间:厨房负责做饭,卧室负责睡觉,卫生间负责...嗯,你懂的。各司其职,生活才能井井有条。
今天咱们就来聊聊 Rust 里的函数,看看它和其他语言有什么不一样,以及为什么 Rust 编译器对函数的"返回值"这么较真。
Rust 里用 fn 关键字来声明函数,读作"function",千万别读成"烦"——虽然刚开始学的时候确实有点烦。
fn main() {
println!("Hello, world!");
}
这个 main 函数你早就见过了。它的特点是:
(),单位类型){} 包起来函数要干活,总得给点东西吧?这就是参数。Rust 的参数声明有个特点:必须指定类型。
fn greet(name: &str, age: u32) {
println!("你好,{}!你今年{}岁了。", name, age);
}
看到没?name: &str 和 age: u32,类型写在变量名后面,用冒号隔开。这跟其他语言很不一样:
语言 | 参数声明 |
|---|---|
Python | def greet(name, age): |
JavaScript | function greet(name, age) { |
Java | void greet(String name, int age) { |
Rust | fn greet(name: &str, age: u32) { |
Rust 的做法:变量名在前,类型在后。读起来更像自然语言:"name 是一个字符串引用"。
函数干完活,总得交出点成果吧?这就是返回值。Rust 用 -> 来指定返回类型:
fn add(a: i32, b: i32) -> i32 {
a + b
}
注意!这里没有 return 关键字!a + b 这一行就是返回值。
重点来了:Rust 里,函数体最后一行表达式(没有分号)就是返回值。如果你加了分号,它就变成了语句,返回值就是 ()(单位类型,相当于"啥也没有")。
fn add_with_return(a: i32, b: i32) -> i32 {
return a + b; // 这样也可以,但 Rustacean 不这么写
}
fn add_expression(a: i32, b: i32) -> i32 {
a + b // 没有分号,这是表达式,作为返回值
}
fn add_wrong(a: i32, b: i32) -> i32 {
a + b; // 有分号,这是语句,返回 (),编译错误!
}
这是 Rust 新手最容易懵的地方。简单说:
1 + 2、x * 3、func()let x = 5;、println!();判断方法:看有没有分号。有分号的是语句,没分号的是表达式。
let x = ; // 语句:let 绑定永远是语句
let y = { // 语句:let 绑定
let a = ; // 语句
let b = ; // 语句
a + b // 表达式:这个块的值是 7
}; // 所以 y = 7
Shadowing(阴影/遮蔽)是 Rust 的一个特色功能:你可以用 let 声明一个同名变量,新变量会"遮蔽"旧变量。
fn main() {
let x = ;
let x = x + ; // 新的 x 遮蔽了旧的 x
let x = x * ; // 又遮蔽了一次
println!("x = {}", x); // 输出:x = 12
}
这看起来跟"可变变量"很像,但有本质区别:
let shadowing | mut 可变变量 |
|---|---|
可以改变类型 | 类型必须一致 |
是"新变量" | 是"同一个变量" |
不能加 &mut | 可以借用为 &mut |
// Shadowing 可以改变类型
let spaces = " ";
let spaces = spaces.len(); // 从 &str 变成 usize
// mut 不能改变类型
let mut spaces = " ";
spaces = spaces.len(); // 编译错误!类型不匹配
生活化类比:
mut 就像你给手机换了个壳,还是同一部手机,只是外观变了fn main() {
let result = add(, );
println!("3 + 5 = {}", result);
// 调用带默认行为的函数
print_greeting("Alice");
print_greeting("Bob");
}
// 两个参数,返回 i32
fn add(a: i32, b: i32) -> i32 {
a + b
}
// 没有返回值(隐式返回 ())
fn print_greeting(name: &str) {
println!("你好,{}!欢迎来到 Rust 的世界!", name);
}
fn add(a: i32, b: i32) {
a + b
}
fn main() {
let result = add(, );
println!("{}", result);
}
编译器错误:
error[E0308]: mismatched types
--> src/main.rs:7:18
|
7 | let result = add(3, 5);
| ^^^^^^^^^ expected `()`, found integer
|
= note: expected unit type `()`
found type `{integer}`
help: consider adding a return type
|
1 | fn add(a: i32, b: i32) -> i32 {
| +++++
人话翻译:编译器说:"你这函数没说要返回啥,我就当它返回 ()(空)。结果你居然返回了个整数?加个 -> i32 会死啊!"
fn add(a: i32, b: i32) -> i32 {
a + b; // 这个分号是万恶之源
}
编译器错误:
error[E0308]: mismatched types
--> src/main.rs:2:5
|
1 | fn add(a: i32, b: i32) -> i32 {
| --- expected `i32` because of return type
2 | a + b;
| ^^^^^^ expected `i32`, found `()`
|
= note: expected type `i32`
found unit type `()`
help: consider removing this semicolon
|
2 | a + b;
| ^
人话翻译:编译器:"你说要返回 i32,结果你加了个分号,变成 () 了。把分号去掉!去掉!去掉!"
我当时就想:我就加个分号而已,至于这么凶吗?后来才明白,这是 Rust 在教我做人——表达式和语句要分清楚。
fn add(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let result = add(3.0, 5.0); // 传了 f64,不是 i32
}
编译器错误:
error[E0308]: mismatched types
--> src/main.rs:6:20
|
6 | let result = add(3.0, 5.0);
| ^^^ expected `i32`, found floating-point number
|
help: change the type of the numeric literal from `{float}` to `i32`
|
6 | let result = add(3, 5.0);
| ^
人话翻译:编译器:"我要 i32,你给我 f64?想啥呢?改成 3 和 5 !"
注意:Rust 不会自动类型转换!这在其他语言里可能行得通,但 Rust 说:"不行就是不行"。
新手常犯的错误:函数明明有返回值,却不写 -> 类型。
fn double(x: i32) {
x * // 编译器:你返回了个寂寞?
}
修复:要么加返回类型,要么加分号让它变成语句。
fn double(x: i32) -> i32 {
x *
}
// 或者
fn double_and_print(x: i32) {
println!("{}", x * ); // 这样就对了,返回 ()
}
let mut x = ;
let x = x + ; // 这里 shadow 了,x 变成不可变了!
x = x + ; // 编译错误:x 不是 mut
人话翻译:第二行 let x 创建了一个新的不可变变量,遮蔽了之前的 mut x。所以第三行报错。
修复:想清楚你要啥。要可变就用 mut,要改类型就用 shadowing。
fn print_name(name: String) {
println!("{}", name);
}
fn main() {
let name = "Alice";
print_name(name); // 编译错误:&str != String
}
修复:用 &str 更灵活。
fn print_name(name: &str) {
println!("{}", name);
}
fn main() {
let celsius = 25.0;
let fahrenheit = celsius_to_fahrenheit(celsius);
println!("{}°C = {}°F", celsius, fahrenheit);
let f = 98.6;
let c = fahrenheit_to_celsius(f);
println!("{}°F = {}°C", f, c);
}
fn celsius_to_fahrenheit(c: f64) -> f64 {
c * 9.0 / 5.0 + 32.0
}
fn fahrenheit_to_celsius(f: f64) -> f64 {
(f - 32.0) * 5.0 / 9.0
}
输出:
25°C = 77°F
98.6°F = 37.00000000000001°C
fn main() {
// 输入是字符串
let input = " 42 ";
// 去掉空格
let input = input.trim();
// 转成数字
let input: i32 = input.parse().expect("不是数字!");
// 翻倍
let input = input * ;
println!("结果是:{}", input); // 输出:结果是:84
}
这个例子展示了 shadowing 的强大:同一个变量名,类型从 &str → &str → i32 → i32,代码可读性极高。
fn main() {
let (sum, diff, product) = calculate_all(, );
println!("和:{}, 差:{}, 积:{}", sum, diff, product);
}
fn calculate_all(a: i32, b: i32) -> (i32, i32, i32) {
(a + b, a - b, a * b)
}
输出:
和:13, 差:7, 积:30
Rust 函数只能返回一个值?没关系,用元组打包带走!

fn,参数必须写类型,返回值用 -> 指定())mut 是原物修改,类型不变下篇预告:函数写多了,代码文件越来越长怎么办?下一篇咱们聊聊模块系统,让你的代码像整理房间一样井井有条!