最近想写一段类似下面的代码:
pub struct Foo<'a, F> /* where F: Fn(&u32) -> bool */ {
u: &'a u32,
f: F
}
impl<'a, F> Foo<'a, F>
where F: Fn(&u32) -> bool
{
pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
where G: Fn(&u32) -> bool
{
Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
}
}
Run Code Online (Sandbox Code Playgroud)
在这里, 的实例Foo表示一段数据 (the u32) 的条件,其中Foo可以通过 由限制较少的数据构建限制性更强的数据new_foo,而不会消耗旧数据。然而,上面的代码并没有像编写的那样编译,而是给出了相当神秘的错误消息:
error[E0308]: mismatched types
--> src/lib.rs:9:52
|
9 | pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(&u32,)>`
found type `std::ops::FnOnce<(&u32,)>`
error: higher-ranked subtype error
--> src/lib.rs:9:5
|
9 | / pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl Fn(&u32) -> bool + '_>
10 | | where G: Fn(&u32) -> bool
11 | | {
12 | | Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
13 | | }
| |_____^
error: aborting due to 2 previous errors
Run Code Online (Sandbox Code Playgroud)
经过大量实验,我确实找到了一种使代码编译的方法,并且我相信它会按预期运行。当声明可以在不依赖于这些边界的情况下编写时,我习惯于在 impls 而不是声明上放置边界的约定,但出于某种原因取消对where上述条款的注释,即将边界F: Fn(&u32) -> bool从 impl复制到Foo自身的声明已解决问题。但是,我不知道为什么这会有所作为(一开始我也不太了解错误消息)。有没有人解释这里发生了什么?
Rust 中存在的唯一子类型是生命周期,因此您的错误(神秘地)暗示存在某种生命周期问题。此外,该错误清楚地指向您的闭包签名,其中涉及两个生命周期:
您已明确声明的闭包本身的生命周期比匿名生命周期长'_;和
它的 argument 的生命周期&u32,您没有明确说明,因此会推断出更高排名的生命周期,就像您声明了以下内容一样:
pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl for<'b> Fn(&'b u32) -> bool + '_>
where G: Fn(&u32) -> bool
Run Code Online (Sandbox Code Playgroud)
使用上面更明确的签名会给出一个(非常)稍微有用的错误:
错误[E0308]:类型不匹配
--> src/lib.rs:9:52
|
9 | pub fn new_foo<G: 'static>(&self, g: G) -> Foo<impl for<'b> Fn(&'b u32) -> bool + '_>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 一种类型比另一种更通用
|
= 注意:预期类型 `std::ops::FnOnce<(&'b u32,)>`
找到类型 `std::ops::FnOnce<(&u32,)>`
至少我们现在可以看到“一种类型比另一种类型更通用”:我们期望一个闭包可以接受任何生命周期的参数,但由于某种原因,Rust 认为我们拥有的是一个闭包,它接受一个可能的参数有一些更受限制的寿命范围。
发生了什么事?那么,该函数的返回值是以下表达式:
Foo { u: self.u, f: move |x| (self.f)(x) && g(x) }
Run Code Online (Sandbox Code Playgroud)
这当然是 的一个实例struct Foo<'a, F>,其中这与块上声明的实例(及其特征绑定)F没有关系。事实上,由于结构定义中impl没有显式绑定,因此编译器必须从表达式本身完全推断出此类型。通过给结构定义一个特征边界,您告诉编译器 的实例(包括上面的表达式)具有实现: 的实例,即参数的生命周期范围是无界的。FFFooFfor<'b> Fn(&'b u32) -> bool&u32
好的,所以编译器需要进行推断F,事实上它确实推断它实现了Fn(&u32) -> bool. 然而,它还不够聪明,无法确定该&u32参数可能被限制在什么生命周期范围内。正如上面@rodrigo 的评论中所建议的,添加显式类型注释表明该参数确实可以具有任何生命周期。
如果实际上对闭包参数的可能生命周期有一些限制,您需要通过将 的定义'b从较高级别的特征边界(即for<'b>上面的返回类型)更改为适合您的任何内容来更明确地表明这一点。情况。
希望一旦chalk完全集成到编译器中,它将能够在不受限制和受限制的情况下执行此推理。与此同时,编译器出于谨慎的考虑而犯了错误,没有做出潜在的错误假设。不过,这些错误肯定会更有帮助!
| 归档时间: |
|
| 查看次数: |
163 次 |
| 最近记录: |