函数指针的类型推导:元组为何会影响推导结果?
为什么元组的存在会改变函数指针的类型推导结果?
Rust里每个函数都有专属的唯一类型,比如add的类型是fn add(i32, i32) -> i32,multiply是fn multiply(i32, i32) -> i32,这俩是完全不同的类型,但它们都能被强制转换到通用的函数指针类型fn(i32, i32) -> i32。
看第二个循环的数组[add, multiply]:编译器会先尝试为数组推导一个统一的元素类型。它发现两个函数都能强制转换到fn(i32, i32) -> i32,所以会自动完成这个转换,让数组元素类型统一,因此不需要手动强制转换。
而第一个循环的数组是[("add", add), ("multiply", multiply)]:这里每个元素是元组,编译器需要先确定每个元组的类型,再统一数组的类型。第一个元组的类型是(&str, fn add(...) -> ...),第二个是(&str, fn multiply(...) -> ...)——这两个元组的类型是不同的(因为元组的第二个元素类型不一样)。Rust的自动强制转换是针对单个值的,没法直接对整个元组做跨类型的统一推导,所以编译器没法自动把两个元组的类型对齐,必须手动把其中一个函数转成通用函数指针,让第一个元组的类型变成(&str, fn(i32,i32)->i32),这样第二个元组里的multiply就能自动转成相同的函数指针类型,数组的类型也就统一了。
有没有更便捷的方式改造第一个循环,无需强制转换?
有两种常用的直观方式:
1. 显式指定数组的类型
直接给数组加上类型标注,告诉编译器我们要的是包含(&str, fn(i32,i32)->i32)元组的数组,编译器会自动把所有函数转成对应的函数指针:
for (name, operation) in [ ("add", add), ("multiply", multiply), ] as [(&str, fn(i32, i32) -> i32); 2] { println!("{} ~~> {}", name, operation(10, 2)); }
2. 使用vec!并指定类型
如果不想写死数组长度,也可以用vec!创建动态数组并指定元素类型:
for (name, operation) in vec![ ("add", add), ("multiply", multiply), ] as Vec<(&str, fn(i32, i32) -> i32)> { println!("{} ~~> {}", name, operation(10, 2)); }
这两种方式都不需要手动给单个函数加as强制转换,比zip的写法更直接。
内容的提问来源于stack exchange,提问作者prog-fh

