Why new allocated returned object has borrowed input parameter?

Hi I have code as following:

    fn foo(_: impl Iterator<Item = impl Borrow<i32>>)->impl Iterator<Item=i32>{
        let mut v = Vec::new();   // new allocated object
        v.push(30);               // has nothing to do with input parameter
        v.into_iter()             //return takes ownership
    }

    fn bar(){
        let input = vec![1,2,3];
        let output0 = foo(input.iter());            //output0 should have nothing to do with input
        let output1 = foo(input.into_iter());       
        let _output2 = foo(output0);                //why rust thinks output0 still borrowed input ??
    }


and rust throws

error[E0505]: cannot move out of `input` because it is borrowed
  --> src/ask.rs:42:27
   |
41 |         let output0 = foo(input.iter());
   |                           ----- borrow of `input` occurs here
42 |         let output1 = foo(input.into_iter());
   |                           ^^^^^ move out of `input` occurs here
43 |         let _output2 = foo(output0);
   |                            ------- borrow later used here

This makes me confused. if change return of foo to Vec, or IntoIter, everything worked

    fn foo2(_: impl Iterator<Item = impl Borrow<i32>>)->Vec<i32>{
        let mut v = Vec::new();
        v.push(30);
        v
    }

    fn bar2(){
        let input = vec![1,2,3];
        let output0 = foo2(input.iter());
        let output1 = foo2(input.into_iter());
        let output2 = foo2(output0.iter());
    }

this works


    fn foo2(_: impl Iterator<Item = impl Borrow<i32>>) -> std::vec::IntoIter<i32> {
        let mut v = Vec::new();
        v.push(30);
        v.into_iter()
    }

    fn bar2(){
        let input = vec![1,2,3];
        let output0 = foo2(input.iter());
        let output1 = foo2(input.into_iter());
        let output2 = foo2(output0);
    }

this also works, why ? Thanks for help

I can't exactly say why, but for some reason, the impl Trait return value seems to keep the lifetime of the input argument attached. I can't tell it not to that either.

Here's a slightly simplified example that still errors:

fn foo<T>(_: T) -> impl 'static + Iterator<Item = i32> {
    std::iter::empty()
}

fn bar() {
    let input = vec![1, 2, 3];
    let o1 = foo(input.iter()); // o1 should be unrelated to input
    let _o2 = foo(input.into_iter());
    let _o3 = foo(o1); // but it keeps input borrowed!
}

Playground link

Maybe someone else has an idea.

And furthermore, the borrow checker errors out even if we remove the last function call - check this:

use std::borrow::Borrow;

fn foo(_: impl Iterator<Item = impl Borrow<i32>>) -> impl Iterator<Item = i32> {
    vec![30].into_iter()
}

fn bar() {
    let input = vec![1, 2, 3];
    let output0 = foo(input.iter()); //output0 should have nothing to do with input
    let output1 = foo(input.into_iter()); // ...but Rust thinks it could somehow use input on drop
}

Playground

Looks like the issue is due to the opaqueness of return type - if I change it to std::vec::IntoIter<i32>, the code compiles.

Even smaller test case:

fn foo<T>(_: T) -> impl Iterator<Item = i32> + 'static {
    vec![2].into_iter()
}

fn bar() {
    let input = vec![1];
    let output0 = foo(&input); //output0 should have nothing to do with input
    let output1 = foo(input);
}
5 Likes