Rust 函数完整知识点详解
涵盖 :基础语法、参数分类、生命周期、泛型、async、unsafe 等
重点:所有权与借用规则、返回值内存规律、高频易错点
目录
- 基础语法结构
- 参数分类
- 函数作用域与位置
- 函数所有权、借用生命周期
- 函数类型、函数指针
- 四类可调用对象
- 参数模式解构
- 泛型函数
- async 异步函数(#九 async-异步函数)
- unsafe 函数(#十 unsafe-函数)
- 函数返回值与栈/堆内存规律
- 高频易错点
- 速记口诀
一、基础语法结构
rust
// 标准格式:fn 函数名 (参数:类型) -> 返回值类型 { 函数体 }
fn add(a: i32, b: i32) -> i32 {
a + b // 表达式,无分号 = 返回值
}
// 调用
let res = add(1, 2);
核心规则
- 关键字
fn定义函数 - 参数必须标注类型,不能省略
- 返回值用
-> 类型声明 - 函数最后一行无分号 为返回表达式;加
;代表语句,返回()空元组
两种返回写法
rust
// 1. 隐式返回(推荐,无分号)
fn one() -> i32 { 1 }
// 2. 显式 return(提前终止函数)
fn max(a: i32, b: i32) -> i32 {
if a > b {
return a;
}
b
}
// 无返回值:省略 ->,等价返回 ()
fn print_log(s: &str) {
println!("{}", s);
}
二、参数分类
1. 普通值参数(所有权转移)
传入 String、Vec 等堆类型会转移所有权,外部变量失效:
rust
fn take_own(s: String) {
println!("{}", s);
}
let s = "hi".to_string();
take_own(s);
// s 失效,不能再使用
2. 引用参数(借用,不转移所有权)
&T:不可变借用,只读&mut T:可变借用,可修改内部数据
rust
fn read(s: &String) {}
fn modify(s: &mut String) {
s.push_str(" add");
}
let mut s = String::new();
modify(&mut s);
read(&s);
提示 :函数参数优先使用
&str而非&String,兼容字面量、String 引用,通用性更强。
3. 可变参数不存在,但有替代方案
Rust 没有 fn foo(...) 可变参数:
- 固定多参数:直接罗列
- 不定数量:传入切片
&[T] - 宏
format!/println!语法糖是宏实现,不是函数可变参数
4. 元组多返回值
函数只能返回单个值,多数据用元组包装:
rust
fn split_num(x: i32) -> (i32, i32) {
(x / 10, x % 10)
}
let (high, low) = split_num(25);
三、函数作用域与位置
| 类型 | 说明 | 调用方式 |
|---|---|---|
| 顶层函数 | 直接写在 crate 根、模块内,全局可导入调用 | func() |
| 关联函数 | 写在 impl 类型 {} 内部,无 &self,常做构造器 new() |
Type::func() |
| 实例方法 | impl 内带 self / &self / &mut self |
instance.func() |
| 内部函数 | 函数内部可定义子函数,仅内部可见 | inner() |
| 闭包 | 匿名函数,捕获上下文 | closure() |
关联函数示例
rust
struct User;
impl User {
// 关联函数
fn new() -> Self { User }
}
let u = User::new();
内部函数示例
rust
fn outer() {
fn inner() {}
inner();
}
四、函数所有权、借用生命周期
1. 生命周期标注(函数引用返回)
若返回引用来自入参,必须标注生命周期,约束借用关系:
rust
// 'a 约束:返回切片生命周期等于输入切片
fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {
if a.len() > b.len() { a } else { b }
}
三条生命周期省略规则(大部分场景不用手动标)
- 函数只有一个引用参数,返回引用自动复用该生命周期
- 方法
&self,返回引用生命周期绑定self - 多个入参、返回其中某一个,必须手动标注
2. 不能返回局部变量的引用
局部变量栈分配,函数结束销毁,返回引用会悬垂引用,编译报错:
rust
fn bad() -> &String {
let s = String::new();
&s // 报错
}
解决方案 :直接返回所有权
String,把数据移到调用方栈/堆。
五、函数类型、函数指针
1. 函数类型签名
rust
// 接收两个 i32 返回 i32 的函数类型:fn(i32, i32) -> i32
let f: fn(i32, i32) -> i32 = add;
println!("{}", f(1, 3));
2. 函数指针作为参数(回调)
rust
fn calc(a: i32, b: i32, op: fn(i32, i32) -> i32) -> i32 {
op(a, b)
}
calc(2, 4, add);
函数指针 vs 闭包
| 类型 | 特点 |
|---|---|
fn 函数指针 |
无捕获环境,轻量 |
| 闭包 | 可捕获外部变量,分三种 Fn/FnMut/FnOnce |
六、四类可调用对象(区分函数/方法/关联函数/闭包)
- 普通自由函数 fn :无依附类型,直接调用
add() - 关联函数 :依附结构体/枚举,无 self,
User::new() - 实例方法 :带 self,实例调用
user.get_name() - 闭包:匿名内联函数,捕获上下文
rust
let add = |a, b| a + b;
add(1, 2);
七、参数模式解构
函数参数可直接解构元组、结构体,简化代码:
rust
// 解构元组参数
fn print_point((x, y): (i32, i32)) {
println!("{} {}", x, y);
}
struct User { name: String }
// 解构结构体
fn greet(User { name, .. }: User) {
println!("{}", name);
}
八、泛型函数
函数支持泛型,实现通用逻辑,配合 trait 约束限制类型:
rust
// 无约束泛型
fn echo<T>(v: T) -> T { v }
// trait 约束:只接收实现 Debug 的类型
use std::fmt::Debug;
fn print_debug<T: Debug>(val: T) {
println!("{:?}", val);
}
// where 语法,多约束更清晰
fn max_val<T>(a: T, b: T) -> T
where
T: PartialOrd,
{
if a > b { a } else { b }
}
九、async 异步函数
async fn 返回 Future,不直接执行,需要执行器 .await 调度:
rust
async fn fetch_data() -> String {
"data".to_string()
}
// 调用必须在 async 上下文 await
async fn run() {
let res = fetch_data().await;
}
十、unsafe 函数
标记 unsafe fn:调用者必须包裹 unsafe {},函数内部包含未定义行为风险操作(裸指针、FFI 等)
rust
unsafe fn raw_ptr(p: *const u8) -> u8 {
*p
}
// 调用必须 unsafe 块
unsafe {
let x = 10;
raw_ptr(&x as *const u8);
}
十一、函数返回值与栈/堆内存规律
| 返回类型 | 内存行为 |
|---|---|
基础类型 (i32/f64/元组/定长数组) |
值拷贝到调用方栈 |
String/Vec/Box 堆类型 |
所有权移动,堆内存转移,无拷贝 |
引用 &T |
仅胖指针/地址拷贝,数据本体不移动 |
| 局部栈变量引用 | ❌ 绝对不能返回(悬垂引用) |
十二、高频易错点
⚠️ 以下易错点需特别注意
- 参数不写类型直接报错,Rust 不支持类型推导函数参数
- 函数末尾加分号会返回
(),和声明的返回值类型不匹配则编译失败 - 传
String会转移所有权 ,后续无法使用;想保留用&String/&str - 返回局部变量引用必报错,要么返回所有权,要么提前外部传入
- 多入参、返回引用时,必须手动标注生命周期
fn函数指针无法捕获外部变量,捕获场景只能用闭包- 无
varargs可变参数 ,不定参数统一用切片&[T]
十三、速记口诀
rust
fn 开头定义函数,参数类型不能省;
尾行无分号返回,return 可提前退出;
传值转移所有权,引用借用不转移;
返回引用标生命周期,局部引用不能返;
泛型配合 trait 约束,async 返回 Future;
关联函数::调用,实例方法点调用。