Rust 1.22.1中.peekable()返回类型不匹配问题咨询
split+peekable会类型不匹配,中转一下就正常? 这问题本质是Rust里**函数项(fn item)和函数指针(fn pointer)**的区别在搞鬼,咱们一步步拆解:
先看你的代码和报错
首先重现你的代码:
use std::str::Split; use std::iter::Peekable; // 正常编译 fn tokenize<'a>(code: &'a str) -> Split<'a, fn(char) -> bool> { code.split(char::is_whitespace) } // 编译报错 fn tokenize_peekable_bad<'a>(code: &'a str) -> Peekable<Split<'a, fn(char) -> bool>> { code.split(char::is_whitespace).peekable() } // 正常编译 fn tokenize_peekable<'a>(code: &'a str) -> Peekable<Split<'a, fn(char) -> bool>> { tokenize(&code).peekable() }
报错信息里明确说了:期望的是Peekable<Split<'a, fn(char) -> bool>>,但实际得到的是Peekable<Split<'_, fn(char) -> bool {std::char::<impl char>::is_whitespace}>>——这里的关键就是fn(char) -> bool(函数指针)和fn(char) -> bool {std::char::is_whitespace}(函数项)是两种不同的类型。
核心区别:函数项 vs 函数指针
- 函数项:每个具体的函数(比如
char::is_whitespace)都有它自己独有的、匿名的类型。你可以把它理解为“这个特定函数的实例”,它比函数指针更具体。 - 函数指针:
fn(char) -> bool是一个通用的类型,可以指代任何符合(char) -> bool签名的函数,相当于一个“函数的引用/指针”。
Rust允许在需要的时候把函数项强制转换为对应的函数指针,但这个转换是有条件的——必须有明确的类型提示触发它。
为什么三个函数的表现不同?
tokenize正常编译:
你的返回类型明确要求Split<'a, fn(char) -> bool>(也就是闭包类型是函数指针)。当你返回code.split(char::is_whitespace)时,Rust看到返回类型需要函数指针,就自动把char::is_whitespace这个函数项强制转换成了fn(char) -> bool类型,所以没问题。tokenize_peekable_bad报错:
这里你直接链式调用split+peekable:- 首先
code.split(char::is_whitespace)返回的Split的闭包类型是函数项fn(char) -> bool {std::char::is_whitespace},而不是函数指针。 - 然后调用
peekable(),得到的Peekable就绑定了这个具体的函数项类型,变成了Peekable<Split<'_, 函数项类型>>。 - 但你的返回类型要求的是
Peekable<Split<'a, 函数指针类型>>,这两个类型不兼容,而且此时已经没有机会再做强制转换了——因为Peekable已经把内部的Split类型固定死了。
- 首先
tokenize_peekable正常编译:
这里你先调用tokenize(&code),而tokenize已经完成了函数项到函数指针的转换,返回的是Split<'a, fn(char) -> bool>(函数指针类型的Split)。之后调用peekable(),得到的Peekable自然就是符合返回类型的Peekable<Split<'a, fn(char) -> bool>>,所以编译通过。
怎么修复tokenize_peekable_bad?
除了用中转函数,你还可以手动强制转换函数项为函数指针,给Rust明确的类型提示:
fn tokenize_peekable_bad_fixed<'a>(code: &'a str) -> Peekable<Split<'a, fn(char) -> bool>> { // 手动把函数项转成函数指针 code.split(char::is_whitespace as fn(char) -> bool).peekable() }
这样split就会返回带有函数指针类型的Split,后续的peekable也能匹配返回类型了。
内容的提问来源于stack exchange,提问作者iafisher

