首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >04-Rust 教程 - 函数基础

04-Rust 教程 - 函数基础

作者头像
LarryLan
发布2026-03-30 18:08:22
发布2026-03-30 18:08:22
1070
举报

Rust 教程 - 函数基础

函数:代码界的"打工人",给啥干啥,干完交活


🎬 引入

你是不是已经写过一些 Rust 代码了?有没有发现,如果把所有代码都塞进 main 函数里,那画面简直不敢看——就像把所有家务活都堆在客厅里做,最后连下脚的地方都没有。

这时候就需要函数登场了。函数就像是你家的各个房间:厨房负责做饭,卧室负责睡觉,卫生间负责...嗯,你懂的。各司其职,生活才能井井有条。

今天咱们就来聊聊 Rust 里的函数,看看它和其他语言有什么不一样,以及为什么 Rust 编译器对函数的"返回值"这么较真。


📌 核心概念

函数声明:fn 是"function"的缩写,不是"烦"

Rust 里用 fn 关键字来声明函数,读作"function",千万别读成"烦"——虽然刚开始学的时候确实有点烦。

代码语言:javascript
复制
fn main() {
    println!("Hello, world!");
}

这个 main 函数你早就见过了。它的特点是:

  • 没有参数(括号里是空的)
  • 没有返回值(或者说返回 (),单位类型)
  • 函数体用花括号 {} 包起来

参数:函数的"食材"

函数要干活,总得给点东西吧?这就是参数。Rust 的参数声明有个特点:必须指定类型

代码语言:javascript
复制
fn greet(name: &str, age: u32) {
    println!("你好,{}!你今年{}岁了。", name, age);
}

看到没?name: &strage: 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 用 -> 来指定返回类型:

代码语言:javascript
复制
fn add(a: i32, b: i32) -> i32 {
    a + b
}

注意!这里没有 return 关键字!a + b 这一行就是返回值。

重点来了:Rust 里,函数体最后一行表达式(没有分号)就是返回值。如果你加了分号,它就变成了语句,返回值就是 ()(单位类型,相当于"啥也没有")。

代码语言:javascript
复制
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;  // 有分号,这是语句,返回 (),编译错误!
}

表达式 vs 语句:Rust 的"灵魂拷问"

这是 Rust 新手最容易懵的地方。简单说:

  • 表达式(Expression):有返回值的东西,比如 1 + 2x * 3func()
  • 语句(Statement):执行某个动作,不返回值,比如 let x = 5;println!();

判断方法:看有没有分号。有分号的是语句,没分号的是表达式。

代码语言:javascript
复制
let x = ;      // 语句:let 绑定永远是语句
let y = {       // 语句:let 绑定
    let a = ;  // 语句
    let b = ;  // 语句
    a + b       // 表达式:这个块的值是 7
};              // 所以 y = 7

Shadowing:同名变量的"合法顶替"

Shadowing(阴影/遮蔽)是 Rust 的一个特色功能:你可以用 let 声明一个同名变量,新变量会"遮蔽"旧变量

代码语言:javascript
复制
fn main() {
    let x = ;
    let x = x + ;  // 新的 x 遮蔽了旧的 x
    let x = x * ;  // 又遮蔽了一次
    println!("x = {}", x);  // 输出:x = 12
}

这看起来跟"可变变量"很像,但有本质区别:

let shadowing

mut 可变变量

可以改变类型

类型必须一致

是"新变量"

是"同一个变量"

不能加 &mut

可以借用为 &mut

代码语言:javascript
复制
// Shadowing 可以改变类型
let spaces = "   ";
let spaces = spaces.len();  // 从 &str 变成 usize

// mut 不能改变类型
let mut spaces = "   ";
spaces = spaces.len();  // 编译错误!类型不匹配

生活化类比

  • Shadowing 就像你换了个新手机,虽然还叫"我的手机",但已经是不同的东西了
  • mut 就像你给手机换了个壳,还是同一部手机,只是外观变了

💻 代码示例

基础示例:一个完整的函数

代码语言:javascript
复制
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);
}

错误示例 1:忘记返回类型

代码语言:javascript
复制
fn add(a: i32, b: i32) {
    a + b
}

fn main() {
    let result = add(, );
    println!("{}", result);
}

编译器错误

代码语言:javascript
复制
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 会死啊!"

错误示例 2:手贱加分号

代码语言:javascript
复制
fn add(a: i32, b: i32) -> i32 {
    a + b;  // 这个分号是万恶之源
}

编译器错误

代码语言:javascript
复制
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 在教我做人——表达式和语句要分清楚

错误示例 3:类型不匹配

代码语言:javascript
复制
fn add(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    let result = add(3.0, 5.0);  // 传了 f64,不是 i32
}

编译器错误

代码语言:javascript
复制
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?想啥呢?改成 35 !"

注意:Rust 不会自动类型转换!这在其他语言里可能行得通,但 Rust 说:"不行就是不行"。


🐛 常见坑点

坑点 1:返回值类型不写

新手常犯的错误:函数明明有返回值,却不写 -> 类型

代码语言:javascript
复制
fn double(x: i32) {
    x * // 编译器:你返回了个寂寞?
}

修复:要么加返回类型,要么加分号让它变成语句。

代码语言:javascript
复制
fn double(x: i32) -> i32 {
    x * 
}

// 或者
fn double_and_print(x: i32) {
    println!("{}", x * );  // 这样就对了,返回 ()
}

坑点 2:shadowing 和 mut 混用

代码语言:javascript
复制
let mut x = ;
let x = x + ;  // 这里 shadow 了,x 变成不可变了!
x = x + ;      // 编译错误:x 不是 mut

人话翻译:第二行 let x 创建了一个新的不可变变量,遮蔽了之前的 mut x。所以第三行报错。

修复:想清楚你要啥。要可变就用 mut,要改类型就用 shadowing。

坑点 3:参数类型写错

代码语言:javascript
复制
fn print_name(name: String) {
    println!("{}", name);
}

fn main() {
    let name = "Alice";
    print_name(name);  // 编译错误:&str != String
}

修复:用 &str 更灵活。

代码语言:javascript
复制
fn print_name(name: &str) {
    println!("{}", name);
}

🎯 实战案例

案例 1:温度转换器

代码语言:javascript
复制
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
}

输出

代码语言:javascript
复制
25°C = 77°F
98.6°F = 37.00000000000001°C

案例 2:用 shadowing 做类型转换

代码语言:javascript
复制
fn main() {
    // 输入是字符串
    let input = "  42  ";
    
    // 去掉空格
    let input = input.trim();
    
    // 转成数字
    let input: i32 = input.parse().expect("不是数字!");
    
    // 翻倍
    let input = input * ;
    
    println!("结果是:{}", input);  // 输出:结果是:84
}

这个例子展示了 shadowing 的强大:同一个变量名,类型从 &str&stri32i32,代码可读性极高

案例 3:多个返回值(用元组)

代码语言:javascript
复制
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)
}

输出

代码语言:javascript
复制
和:13, 差:7, 积:30

Rust 函数只能返回一个值?没关系,用元组打包带走!


🧠 思维导图

04-函数基础
04-函数基础

📝 小结

  1. 函数声明用 fn,参数必须写类型,返回值用 -> 指定
  2. 最后一行表达式是返回值,别手贱加分号(除非你真想返回 ()
  3. 表达式有值,语句无值——这是 Rust 的灵魂
  4. Shadowing 是合法顶替,可以改类型;mut 是原物修改,类型不变
  5. Rust 不自动类型转换,该写啥类型就写啥类型

下篇预告:函数写多了,代码文件越来越长怎么办?下一篇咱们聊聊模块系统,让你的代码像整理房间一样井井有条!


🔗 参考资料

  • Rust Book - 函数
  • Rust By Example - 函数
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Rust 教程 - 函数基础
    • 🎬 引入
    • 📌 核心概念
      • 函数声明:fn 是"function"的缩写,不是"烦"
      • 参数:函数的"食材"
      • 返回值:函数的"成品"
      • 表达式 vs 语句:Rust 的"灵魂拷问"
      • Shadowing:同名变量的"合法顶替"
    • 💻 代码示例
      • 基础示例:一个完整的函数
      • 错误示例 1:忘记返回类型
      • 错误示例 2:手贱加分号
      • 错误示例 3:类型不匹配
    • 🐛 常见坑点
      • 坑点 1:返回值类型不写
      • 坑点 2:shadowing 和 mut 混用
      • 坑点 3:参数类型写错
    • 🎯 实战案例
      • 案例 1:温度转换器
      • 案例 2:用 shadowing 做类型转换
      • 案例 3:多个返回值(用元组)
    • 🧠 思维导图
    • 📝 小结
    • 🔗 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档