2

Why does this, with no type annotation on the closure, compile?

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

But this doesn't?

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

Is the inferred type not &Vec<String>? Or do I need to annotate the lifetime too, as this seems to be about the closure not living long enough?

The error for in the latter snippet is

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());
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    FYI - [Why is it discouraged to accept a reference to a String (&String) or Vec (&Vec) as a function argument?](http://stackoverflow.com/q/40006219/155423) – Shepmaster Dec 08 '16 at 21:46
  • 1
    @Shepmaster thanks, quite right. This is a cut down version of something else and I didn't clean that up. The argument was originally `&BTreeMap` –  Dec 08 '16 at 21:59

1 Answers1

4

The compiler is worried that your code can produce dangling pointers. When you leave off the type annotation, the compiler correctly infers the lifetime of the inner references in relation to the outer references.

However, when you annotate the type and don't explain to the compiler how the lifetimes of the internal references relate to the outer references (that is, how the words: &Vec<String>/internal references relate to the input: &Vec<Vec<String>>/outer references), the compiler freaks out.

The easy fix, is to let the compiler know that the inner references live at least the same lifetime as the outer one:

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

Hopefully that makes sense. In Rust, a reference can't outlive what it references. In the eyes of the compiler, your input reference might go away before your words references do (which is bad because words references items inside input). So if you're not going to let the compiler infer the lifetimes, you need to be explicit about it.

Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • good answer!! now I have a question. would you answer it plz? how can I see infered type or lifecycle by compile? – hanbumpark Dec 08 '16 at 22:59
  • Not that I know of, to be honest. Within the `transform` function, the elided lifetimes will be the same. The introduction of the closure using a reference though is what causes the compiler to not know what to do. If you look at the MIR ([say, on the Playground](https://play.rust-lang.org/?gist=8e091434fffaa4bc28e723e5a88203fd&version=nightly&backtrace=0)), you can see that the compiler settles on `'_` as the lifetime it infers and it uses it everywhere. When you introduce a compiler error though.. you can't see this. – Simon Whitehead Dec 08 '16 at 23:40
  • I'd be interested in a reference for how the inference (especially on closures, which seem a bit special) works... – Chris Emerson Dec 08 '16 at 23:41
  • @ChrisEmerson The MIR was what I used as a reference. Specifically, [it appears to declare an `Iter` instance where the lifetime used matches that of the output parameter](https://play.rust-lang.org/?gist=69f44cda87a709e920a231777ccfe97a&version=nightly&backtrace=0), in the MIR generated for the closure. If I have misinterpreted that I apologise. – Simon Whitehead Dec 09 '16 at 00:16
  • I haven't really looked into MIR yet - I should. But for detail questions like this I really miss an authoritative language spec (as opposed to what the current compiler does, which is useful but might not be as intended, e.g. might include bugs). – Chris Emerson Dec 09 '16 at 00:26
  • @ChrisEmerson A fair point. There is probably something in the _rvalue/lvalue_ parts of the Rust reference that explains how the compiler will infer the lifetime given its surrounding environment. If I get a chance later I will try and find it. – Simon Whitehead Dec 09 '16 at 00:32