Rust 代码如下:
trait Test: Sized {
fn test(self, f: impl FnOnce(Self)) {
f(self)
}
}
impl<T> Test for T {}
fn foo<T>(f: impl FnOnce(T)) {
|x| x.test(f);
}
编译错误:
error[E0282]: type annotations needed
--> src/lib.rs:9:6
|
9 | |x| x.test(f);
| ^ - type must be known at this point
|
help: consider giving this closure parameter an explicit type
|
9 | |x: _| x.test(f);
| +++
For more information about this error, try `rustc --explain E0282`.
error: could not compile `playground` due to previous error
x
传入到了 f
里,被当成闭包 f
的参数,所以类型自然应该是 T
呀!
这个闭包参数的类型需要声明,是从原理上就无法推导,还是 Rust 编译器可以改进的?
1
共 13 条评论, 1 页
评论区
写评论给不知道的人一个提醒,楼主已经在官方论坛提了同样的问题
The closure parameter type needs to be declared, can the compiler improve this?
而且我觉得那里的回答很棒了。
基本上就是 Rust 类型推断方向的事情,它当然不完美(而且规则并未被明确 —— 所以可以改进),但至少是保守的(导致的结果无非就是多加类型标注来明确你需要的类型)。
https://rustc-dev-guide.rust-lang.org/closure.html 可以看下rustc的文档
--
👇
hzqd: 我加了您说的这个trait,四种情况均无法编译,报冲突了。
但如果我只给结构体关联了同名的函数:
四种情况均能编译通过,这是为什么呢?
--
👇
wangbyby: - 对于情况四,你可以加个
看看结果.
情况3中,从
f(x)
可以推出x的类型为T,但如果反过来先x.test(f)
,那么x的类型在此时是无法推出的,即类型推导与语句顺序有关。情况4,给结构体关联了同名的函数是指什么?
以上代码是无法通过编译的
我加了您说的这个trait,四种情况均无法编译,报冲突了。
但如果我只给结构体关联了同名的函数:
四种情况均能编译通过,这是为什么呢?
--
👇
wangbyby: - 对于情况四,你可以加个
看看结果.
看看结果.
--
👇
hzqd: 请问您如何解释以下四种编译通过的情况?
情况一:
情况二:
情况三:
情况四:
它们是如何避免“其他
trait
和struct
存在同名方法”而直接通过了编译呢?--
👇
Pikachu: x.test可以指向的是任意的函数。假设std或者core里有个同名的函数test,编译器不可能知道你用的是哪个函数,也就没法根据函数签名做出类型约束。
另外,对于情况三来讲,如下等价代码不能通过编译,这又是为什么?
因为
Rust
具备全局类型推导的特性,它完全可以根据else
代码块的内容反推x
的类型。但这段代码依然获得编译错误,其故何如?
请问您如何解释以下四种编译通过的情况?
情况一:
情况二:
情况三:
情况四:
它们是如何避免“其他
trait
和struct
存在同名方法”而直接通过了编译呢?--
👇
Pikachu: x.test可以指向的是任意的函数。假设std或者core里有个同名的函数test,编译器不可能知道你用的是哪个函数,也就没法根据函数签名做出类型约束。
x.test可以指向的是任意的函数。假设std或者core里有个同名的函数test,编译器不可能知道你用的是哪个函数,也就没法根据函数签名做出类型约束。
如果你想明确指定这里用的是Test::test函数,你得这么写
当然这里会有warning,因为定义了一个closure但没有使用。但是最起码编译能通过,说明类型没错。
--
👇
hzqd: 但
x
是闭包f
的参数呀,闭包f
的参数类型就是T
,为何说x
可以为任意类型?如果你是指:
T
是泛型,而不是具体类型。那么,给x
显式声明泛型T
依然可以通过编译,又要如何解释?但
x
是闭包f
的参数呀,闭包f
的参数类型就是T
,为何说x
可以为任意类型?如果你是指:
T
是泛型,而不是具体类型。那么,给x
显式声明泛型T
依然可以通过编译,又要如何解释?在原错误写法中,x可以为任意类型,不一定为T类型,x的类型从原理上就无法推导。
--
👇
hzqd: 你可以解释一下为什么加上返回类型就可以通过编译吗?
你可以解释一下为什么加上返回类型就可以通过编译吗?
首先,
Pipe
这个trait
很奇怪,泛型参数为何不在pipe
函数上而要放在trait
上?这样可能更好一点。
其次,
main
函数只放一个闭包表达式是什么意思?不太明白。如果只是想示意这个闭包用户,将其绑定到一个变量上,语义可能会更明确一些。
最后回到你的问题,即
|x| x.pipe(test)
的类型问题。正如上面所写,那么你期望
_clousure
是什么类型?如果要推导类型,只能推出来如下信息:test
的类型是fn (fn (?T))
.Pipe::pipe
的类型中,由test
为pipe
的第二个参数,可知x
的类型为fn (?T)
,x.pipe(test)
的类型(即pipe
中的泛型参数R
)为()
,|x| x.pipe(test)
的类型为f(?impl Pipe)
。可见,此时仍然不知道
x
的类型。所以必须显式地标注x的类型。