🧠 Rust 柯里化从零讲透:看不懂 fn add(x) -> impl Fn(y)
的同学点进来!
🍔 一、什么是柯里化?先用一个超好懂的生活比喻
假设你在点一个汉堡:
text
你说:我要点一个鸡腿汉堡!
店员说:好的,请先选肉 → 鸡腿
再选酱料 → 辣酱
最后选芝士 → 加!
是不是你并没有一次性说完,而是一步一步选项配置完成?每次传一个参数。
这就是"柯里化"的思想:
把一个需要多个参数的函数,变成"一层一层传一个参数"的函数结构。
💡 二、我们来对比看:正常函数 VS 柯里化函数
rust
// 普通函数(一次性传两个参数)
fn add(x: i32, y: i32) -> i32 {
x + y
}
调用方式是:
rust
let result = add(2, 3); // 直接给两个参数,得到结果 5
而柯里化版本写法是这样的:
rust
fn add(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
🔍 三、这一句到底怎么读?我们逐字逐句拆解!
📘 原始代码:
rust
fn add(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
🧩 第一步:函数签名部分解读
rust
fn add(x: i32)
意思是:这个函数叫 add
,它接收一个参数 x
,类型是 i32
🧩 第二步:箭头 ->
后面部分
rust
-> impl Fn(i32) -> i32
这部分表示:
函数的返回值是 一个"能接收 i32,并返回 i32"的函数
说人话就是:
你调用
add(10)
得到的,并不是一个数字,而是一个函数!
而这个函数就像这样:
rust
|y| 10 + y
这个函数"等着你传第二个参数 y
",然后它会用之前的 x = 10
加上 y
,返回结果。
🧩 第三步:move |y| x + y
是什么意思?
这句其实是 Rust 的"闭包写法",也叫"匿名函数":
rust
move |y| x + y
拆开解释:
部分 | 解释 |
---|---|
move |
让闭包把外部变量(比如 x )带进来,封装进去 |
` | y |
x + y |
闭包的执行体逻辑:x 和 y 相加 |
所以这个闭包表示:
"我是一个能接受
y
的函数,我知道外部的x
,然后返回x + y
的结果。"
🧪 举个完整例子,来看怎么使用这个柯里化函数:
rust
fn add(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
fn main() {
let add_10 = add(10);
// ↑ 第一步,传入参数 x = 10,返回一个闭包
// 这个闭包相当于 |y| 10 + y
let result = add_10(5);
// ↑ 第二步,再传入 y = 5
// 执行的是:10 + 5 = 15
println!("结果是: {}", result); // 输出 结果是: 15
}
🧱 四、图解:柯里化是怎么"层层返回"的?
fn add(x)
↓
返回一个函数 |y| {
用到之前的 x + 当前的 y
}
你可以理解为:第一次函数调用"定制好了逻辑",但还没执行;第二次才执行。
💭 五、为什么我们明明没有看到 y 参数,却能传 y?
这是**"闭包"**的关键作用。
闭包 move |y| x + y
是个"函数",你可以存它、传它、执行它。
当你写:
rust
let add_10 = add(10);
这个 add_10
是个函数,你可以像下面这样用它:
rust
add_10(1)
add_10(2)
add_10(100)
每次传进去的就是参数 y
!
这个 y
就是在你真正执行闭包的时候才"出现"。
✅ 六、你现在可以这样理解这句代码:
rust
fn add(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
🔹 它是一个"做加法的函数工厂":
你告诉它
x
它就给你返回一个"只差 y"的函数。你什么时候需要加
y
,再传进去,它才给你结果。
🧰 七、为什么要这么麻烦搞柯里化?
因为柯里化有这些强大优势:
应用场景 | 优势 |
---|---|
🎯 提前绑定参数 | 可以预设 x,只改 y,比如 add_10 = add(10) |
🧱 组合函数 | 可以把多个闭包组合成管道式处理函数 |
🧩 配置默认项 | 比如封装 IP,后续只改端口(见下文) |
📦 构造工厂函数 | 像"做函数的函数",可复用、易测试 |
🔁 八、再看两个实用场景
🟢 配置化参数:绑定一部分值
rust
fn connect(ip: &str) -> impl Fn(u16) -> String {
let ip = ip.to_string(); // 复制字符串所有权进闭包
move |port| format!("连接 {}:{}", ip, port)
}
用法:
rust
let local = connect("127.0.0.1");
println!("{}", local(8080)); // 连接 127.0.0.1:8080
println!("{}", local(3000)); // 连接 127.0.0.1:3000
✅ 优势:IP 只写一次,port 可动态传。更灵活、结构清晰
🟢 函数组合:先加再乘
rust
fn add(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
fn mul(x: i32) -> impl Fn(i32) -> i32 {
move |y| x * y
}
fn main() {
let step1 = add(2); // +2
let step2 = mul(3); // *3
let result = step2(step1(4)); // (4 + 2) * 3 = 18
println!("组合结果: {}", result);
}
📘 九、总结:你现在该怎么读懂这一句代码?
rust
fn add(x: i32) -> impl Fn(i32) -> i32 {
move |y| x + y
}
✅ 翻译成人话:
"我这个函数
add
,先让你告诉我x
,我会返回一个新的函数,这个新函数能接收y
,然后把x + y
返回。"
它是个两步操作函数,每步只干一件事。
🧠 十、关键记忆方法
方法 | 说明 |
---|---|
📦 想象闭包是"能记住变量的盒子" | `move |
🔁 柯里化就是函数拆分,每层一个参数 | 从 f(x, y) 变成 f(x)(y) |
🧠 多举例,多写"函数返回函数"的结构 | 自己模仿写"加法器、乘法器、配置函数"等 |
如果你想,我也可以做一个动画演示图,帮助你形象化理解"add(10) 返回函数"的过程!