【问题标题】:Type mismatches resolving a closure that takes arguments by reference类型不匹配解决了通过引用获取参数的闭包
【发布时间】:2016-10-21 00:00:29
【问题描述】:

我在尝试编译下面的 Rust 代码时遇到了一对奇怪的错误。在寻找其他有类似问题的人时,我遇到了another question with the same combination of (seemingly opposing) errors,但无法将那里的解决方案推广到我的问题。

基本上,我似乎遗漏了 Rust 所有权系统的一个微妙之处。在这里尝试编译(非常精简的)代码:

struct Point {
    x: f32,
    y: f32,
}

fn fold<S, T, F>(item: &[S], accum: T, f: F) -> T
where
    F: Fn(T, &S) -> T,
{
    f(accum, &item[0])
}

fn test<'a>(points: &'a [Point]) -> (&'a Point, f32) {
    let md = |(q, max_d): (&Point, f32), p: &'a Point| -> (&Point, f32) {
        let d = p.x + p.y; // Standing in for a function call
        if d > max_d {
            (p, d)
        } else {
            (q, max_d)
        }
    };

    fold(&points, (&Point { x: 0., y: 0. }, 0.), md)
}

我收到以下错误消息:

error[E0631]: type mismatch in closure arguments
  --> src/main.rs:23:5
   |
14 |     let md = |(q, max_d): (&Point, f32), p: &'a Point| -> (&Point, f32) {
   |              ---------------------------------------------------------- found signature of `for<'r> fn((&'r Point, f32), &'a Point) -> _`
...
23 |     fold(&points, (&Point { x: 0., y: 0. }, 0.), md)
   |     ^^^^ expected signature of `for<'r> fn((&Point, f32), &'r Point) -> _`
   |
   = note: required by `fold`

error[E0271]: type mismatch resolving `for<'r> <[closure@src/main.rs:14:14: 21:6] as std::ops::FnOnce<((&Point, f32), &'r Point)>>::Output == (&Point, f32)`
  --> src/main.rs:23:5
   |
23 |     fold(&points, (&Point { x: 0., y: 0. }, 0.), md)
   |     ^^^^ expected bound lifetime parameter, found concrete lifetime
   |
   = note: required by `fold`

(A Rust Playground link for this code, for convenience.)

在我看来,我提供给 fold 的函数应该正确地进行类型检查......我在这里遗漏了什么,我该如何修复它?

【问题讨论】:

  • 两个注意事项:99.99% 的时间使用&amp;[T] 而不是&amp;Vec&lt;T&gt;。另外,当一个空向量被传递给它时,你的test 函数会发生什么?该参考将在哪里出现?
  • @Shepmaster 所以我实际上使用Vec&lt;T&gt; 来代替我正在处理的自定义函数式列表类型,只是为了保持问题简单并专注于我的错误米得到。此外,在我的代码的非精简版本中,一个空列表 panic!s 并表示无事可做。基本上,我试图将代码减少到我仍然会收到错误消息而忽略任何无关内容的程度。
  • 那很好,减少问题也很棒!但是,当前的 MCVE 尝试返回对局部变量的引用,这将导致生命周期错误,可能阻止我给你答案,因为我无法编译它。而且你不应该让你的手指输入&amp;Vec&lt;T&gt;,所以它甚至不应该出现在一个例子中^_^。
  • 出于好奇,为什么 &[T] 优于 &Vec

标签: rust lifetime ownership


【解决方案1】:

简短的版本是,如果闭包是内联编写或存储为变量,则推断的生命周期之间存在差异。内联编写闭包并删除所有无关的类型:

fn test(points: &[Point]) -> (&Point, f32) {
    let init = points.first().expect("No initial");
    fold(&points, (init, 0.), |(q, max_d), p| {
        let d = 12.;
        if d > max_d {
            (p, d)
        } else {
            (q, max_d)
        }
    })
}

如果您确实需要带外关闭,请查看How to declare a lifetime for a closure argument?

此外,我必须从输入数组中提取first 值——您不能返回对局部变量的引用。该方法不需要生命周期参数;它们将被推断出来。

要真正编译代码,您需要提供有关fold 方法的更多信息。具体来说,您必须指出传递给闭包的引用与传入的参数具有相同的生命周期。否则,它可能只是对局部变量的引用:

fn fold<'a, S, T, F>(item: &'a [S], accum: T, f: F) -> T
where
    F: Fn(T, &'a S) -> T,
{
    f(accum, &item[0])
}

相关的 Rust 问题是#41078

【讨论】:

  • 因此,内联传递的闭包的类型和生命周期是从它们被传递到的函数中推断出来的,但是存储为变量的闭包会尝试在没有该上下文的情况下确定类型和生命周期?
  • @Trevor 我的答案有点含糊,因为我不确定。稍微挥手,我可能会说“早期”或“晚期”界限,但我不确定这个术语有多准确,更不用说与真正原因的关系了。好消息是这样的内联闭包被认为是惯用的。
  • 很公平!感谢你的帮助;指出我的局部变量(以及与之相关的短暂生命周期)帮助我解决了下一个问题,由于我最初询问的错误,我什至看不到。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多