【问题标题】:Why does a closure passed to flat_map without a type annotation compile, while one with an annotation does not?为什么传递给 flat_map 的闭包没有类型注解可以编译,而带有注解的闭包不能编译?
【发布时间】:2016-12-08 21:39:35
【问题描述】:

为什么在闭包上没有类型注释的情况下编译?

fn transform(input: &Vec<Vec<String>>) {
    input.iter().flat_map(|words| words.iter());
}

但这不是吗?

fn transform(input: &Vec<Vec<String>>) {
    input.iter().flat_map(|words: &Vec<String>| words.iter());
}

推断的类型不是&amp;Vec&lt;String&gt;吗?还是我也需要注释生命周期,因为这似乎是关于闭包的生命周期不够长?

后面的sn-p中的错误是

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
 --> src/main.rs:2:55
  |
2 |     input.iter().flat_map(|words: &Vec<String>| words.iter());
  |                                                       ^^^^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 2:48...
 --> src/main.rs:2:49
  |
2 |     input.iter().flat_map(|words: &Vec<String>| words.iter());
  |                                                 ^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
 --> src/main.rs:2:49
  |
2 |     input.iter().flat_map(|words: &Vec<String>| words.iter());
  |                                                 ^^^^^
note: but, the lifetime must be valid for the method call at 2:4...
 --> src/main.rs:2:5
  |
2 |     input.iter().flat_map(|words: &Vec<String>| words.iter());
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so type `fn(std::slice::Iter<'_, std::vec::Vec<std::string::String>>, [closure@src/main.rs:2:27: 2:61]) -> std::iter::FlatMap<std::slice::Iter<'_, std::vec::Vec<std::string::String>>, std::slice::Iter<'_, std::string::String>, [closure@src/main.rs:2:27: 2:61]> {<std::slice::Iter<'_, std::vec::Vec<std::string::String>> as std::iter::Iterator>::flat_map::<std::slice::Iter<'_, std::string::String>, [closure@src/main.rs:2:27: 2:61]>}` of expression is valid during the expression
 --> src/main.rs:2:5
  |
2 |     input.iter().flat_map(|words: &Vec<String>| words.iter());
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

【问题讨论】:

标签: rust


【解决方案1】:

编译器担心您的代码会产生悬空指针。当您不使用类型注释时,编译器会正确地推断出内部引用相对于外部引用的生命周期。

但是,当您注释类型并且向编译器解释内部引用的生命周期如何与外部引用相关时(即,words: &amp;Vec&lt;String&gt;/内部引用如何相关到input: &amp;Vec&lt;Vec&lt;String&gt;&gt;/外部引用),编译器吓坏了。

简单的解决方法是让编译器知道内部引用的生命周期至少与外部引用相同:

fn transform<'a>(input: &'a Vec<Vec<String>>) {
    input.iter().flat_map(|words: &'a Vec<String>| words.iter());
}

希望这是有道理的。在 Rust 中,引用不能超过它所引用的内容。在编译器看来,您的input 引用可能会在您的words 引用之前消失(这很糟糕,因为words 引用了input 中的项目)。因此,如果您不打算让编译器推断生命周期,则需要明确说明。

【讨论】:

  • 好答案!!现在我有一个问题。 lz你会回答吗?如何通过编译查看推断的类型或生命周期?
  • 说实话,我不知道。在transform 函数中,省略的生命周期将是相同的。使用引用引入闭包是导致编译器不知道该做什么的原因。如果您查看 MIR (say, on the Playground),您可以看到编译器将 '_ 作为它推断的生命周期并在任何地方使用它。但是,当您引入编译器错误时..您看不到这一点。
  • 我有兴趣了解推理的工作原理(尤其是在闭包上,这似乎有点特别)...
  • @ChrisEmerson MIR 是我用作参考的。具体来说,it appears to declare an Iter instance where the lifetime used matches that of the output parameter,在为闭包生成的 MIR 中。如果我误解了,我道歉。
  • 我还没有真正研究过 MIR - 我应该。但是对于像这样的细节问题,我真的很想念一个权威的语言规范(与当前编译器所做的相反,它很有用但可能不是预期的那样,例如可能包含错误)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-17
  • 1970-01-01
相关资源
最近更新 更多