ZhangHanDong / tao-of-rust-codes

《Rust编程之道》随书源码
https://ruststudy.github.io/tao_of_rust_docs/tao_of_rust/
MIT License
1.18k stars 170 forks source link

函数返回闭包为何不知道具体的返回类型? #263

Closed zydxhs closed 4 years ago

zydxhs commented 4 years ago

页码与行数


疑问

原文:代码清单2-15:闭包作为返回值的情况

fn two_times_impl() -> impl Fn(i32) -> i32 {
    let i = 2;
    move |j| j * i
}
fn main() {
    let result = two_times_impl();
    assert_eq!(result(2), 4);
}

在代码清单2-15中使用了 impl Fn(i32) -> i32 作为函数的返回值,它表示实现 Fn(i32)->i32的类型。在函数定义时并不知道具体的返回类型,但是在函数调用时,编译器会推断出来。

在函数定义时并不知道具体的返回类型怎么理解?

函数返回像 i32 这样的类型,在函数定义时是知道返回值的;而返回闭包时,在函数定义时不知道其返回值?为什么返回闭包时,就不知道具体的返回类型了?

请张老师或其它知道的道友讲解下,在先行此谢过。

ZhangHanDong commented 4 years ago

@zydxhs 返回的闭包这里相当于是返回值位置上的泛型,不是具体类型。只有在实际调用的时候,才能单态化为具体类型。

zydxhs commented 4 years ago

还是不太清楚。疑问点:

  1. 定义时不知道返回类型。函数签名是 impl Fn(i32) -> i32,可以理解为返回一个函数,参数为一个i32,返回值也是i32。在这里,个人感觉这已经是一个具体的函数指针类型了。在C++知道返回值、参数的类型,就可以声明一个具体的函数指针了,为啥这里行不通?是因为编译还不够聪明?

  2. 只有在实际调用的时候,才能单态化为具体类型实际调用是指在 main 函数中的assert_eq!语言中的调用吗?如果在这里单态化为具体类型,这也存在问题。这里只能传入 i32 类型的参数,不能传其它类型的参数,泛型实例化又从何说起呢?

ZhangHanDong commented 4 years ago

@zydxhs

函数签名是 impl Fn(i32) -> i32,可以理解为返回一个函数

你的理解是错的。这里返回的是一个闭包,是trait。建议你不要在这里钻牛角尖了,暂时放下问题,往后面学习吧。

实际调用是指在 main 函数中的assert_eq!语言中的调用吗?

是的。 这里的实例化不是i32实例化,而是闭包实例化。

ZhangHanDong commented 4 years ago

@zydxhs 第二章的目的是帮你在回顾和梳理一下语法,不要钻牛角尖,尽快往后面看。

zydxhs commented 4 years ago

3ks