如何借用检查“impl Trait”类型的返回值?

rub*_*ubo 7 rust borrow-checker

以下代码无法编译:

fn foo<'a, F: Fn() -> &'a str>(vec: Vec<i32>, fun: F) -> impl Iterator<Item = i32> {
    println!("{}", fun());
    vec.into_iter()
}

fn main() {
    let s = "hello, world!".to_string();
    let iter = foo(vec![1, 2, 3], || &s);
    drop(s);

    for x in iter {
        println!("{}", x);
    }
}
Run Code Online (Sandbox Code Playgroud)
error[E0505]: cannot move out of `s` because it is borrowed
  --> src/main.rs:9:10
   |
8  |     let iter = foo(vec![1, 2, 3], || &s);
   |                                   --  - borrow occurs due to use in closure
   |                                   |
   |                                   borrow of `s` occurs here
9  |     drop(s);
   |          ^ move out of `s` occurs here
10 | 
11 |     for x in iter {
   |              ---- borrow later used here
Run Code Online (Sandbox Code Playgroud)

如果我将foo的签名替换为

fn foo<'a, F: Fn() -> &'a str>(vec: Vec<i32>, fun: F) -> <Vec<i32> as IntoIterator>::IntoIter {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这让我相信impl Trait类型的借用检查更加保守:编译器假设返回的对象捕获,fun即使它没有捕获。

然而,这个有趣的例子编译得很好:

fn foo(s: &str) -> impl Iterator<Item = i32> {
    println!("{}", s);
    vec![1, 2, 3].into_iter()
}

fn main() {
    let s = "hello, world!".to_string();
    let iter = foo(&s);
    drop(s);

    for x in iter {
        println!("{}", x);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里编译器似乎没有假设返回的impl Iterator<Item = i32>借用s.

返回impl Trait类型究竟是如何借用检查的?他们什么时候假设借用其他函数参数,就像在第一种情况下一样?什么时候假设他们不这样做,就像在后一种情况下一样?

Dan*_*all 3

我相信这个问题评论讲述了这里的故事。听起来这是类型系统有意限制的保守性,但我同意问题作者的观点,即能够选择退出是件好事:

这种行为的核心原因是返回位置中 impl Trait 的差异行为:返回的 impl Trait 始终在所有通用输入参数上有所不同,即使在技术上没有使用。这样做是为了,如果您更改返回的公共 API 的内部实现,impl Trait则不必担心向 API 引入额外的方差参数。这可能会破坏下游代码,因此对于 Rust 的 semver 系统来说是不可取的。