为什么 Rust 不能在简单的闭包中推断出正确的生命周期,或者推断它们是冲突的



我在 Rust 文档中没有找到任何可以解释生命周期 elision 如何应用于闭包的规则。让我们举一个简单的例子:

fn foo(s: &str) {
    let id = |x: &str| x;
    println!("{}", id(s));
}
fn main() {
    foo("string");
}

我认为 foo 函数中的闭包将类似于以下代码:

fn foo(s: &str) {
    struct Id;  // A helper structure for closure
    impl Id {
        fn id(self: Self, x: &str) -> &str { &x }
    }
    let id = Id; // Creating a closure
    println!("{}", id.id(s));
}

后者工作正常,但前者无法编译并生成有关生存期要求冲突的长错误消息:

t3.rs:2:24: 2:25 error: cannot infer an appropriate lifetime due to conflicting requirements [E0495]
t3.rs:2     let id = |x: &str| x;
                               ^
t3.rs:2:24: 2:25 note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 2:23...
t3.rs:2     let id = |x: &str| x;
                               ^
t3.rs:2:24: 2:25 note: ...so that expression is assignable (expected `&str`, found `&str`)
t3.rs:2     let id = |x: &str| x;
                               ^
<std macros>:3:11: 3:36 note: but, the lifetime must be valid for the expression at 3:10...
<std macros>:3 print ! ( concat ! ( $ fmt , "n" ) , $ ( $ arg ) * ) ) ;
                         ^~~~~~~~~~~~~~~~~~~~~~~~~
<std macros>:2:25: 2:56 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
t3.rs:3:5: 3:27 note: in this expansion of println! (defined in <std macros>)
<std macros>:3:11: 3:36 note: ...so type `(&&str,)` of expression is valid during the expression
<std macros>:3 print ! ( concat ! ( $ fmt , "n" ) , $ ( $ arg ) * ) ) ;
                         ^~~~~~~~~~~~~~~~~~~~~~~~~
<std macros>:2:25: 2:56 note: in this expansion of format_args!
<std macros>:3:1: 3:54 note: in this expansion of print! (defined in <std macros>)
t3.rs:3:5: 3:27 note: in this expansion of println! (defined in <std macros>)
error: aborting due to previous error

我想知道为什么 Rust 无法像我上面写的那样在简单的闭包中推断出适当的生命周期。此外,为什么编译器认为存在生存期的冲突要求。

在闭包中指定参数的类型或返回类型,并且该类型是引用时,编译器会对隐式生存期参数设置错误的期望,并且无法在闭包上显式定义生存期参数。这是一个已知问题。解决方法是省略参数的类型或返回类型,并让编译器推断所有内容。

fn foo(s: &str) {
    let id = |x| x;
    println!("{}", id(s));
}
fn main() {
    foo("string");
}

如果仍然需要提供类型提示,可以在闭包中使用let绑定:

fn foo(s: &str) {
    let id = |x| { let y: &str = x; y };
    println!("{}", id(s));
}
fn main() {
    foo("string");
}

闭包无法从方法签名推断生存期。实际上,您在方法签名中定义了一个生存期,假设它是隐式'a,另一个生存期,隐式'b在闭包中。

匹配生命周期,它将编译。

fn foo<'a>(s: &'a str) {
    let id = |x: &'a str| x;
    println!("{}", id(s))
}
fn main() {
    foo("string");
}

相关内容

最新更新