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

7

我在 Rust 文档中没有发现任何规则能够解释生命周期省略如何应用于闭包。让我们来看一个简单的例子:

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 不能推断出像我上面写的简单闭包中正确的生命周期。此外,为什么编译器认为有寿命的要求存在冲突。

移除 : &str 就可以运行了。那里的 &str 并不是你想象中的意思。我现在没时间解释,因为我应该去睡觉了。 - Chris Morgan
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - svat
@ChrisMorgan:如果你有时间的话,能否解释一下发生了什么情况呢?我感觉可能是由于一个推断出来的 for<'a>,但还不太清楚……而且在20个小时后没有得到答案,似乎我不是唯一不确定发生了什么的人^^ - Matthieu M.
2个回答

3

当您指定参数或闭包返回类型的类型,并且该类型是一个引用时,编译器会对隐式生命周期参数设置错误的期望值,并且没有办法在闭包中明确定义生命周期参数。 这是一个已知的问题。 解决方法是省略参数类型或返回类型,并让编译器自动推断所有内容。

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");
}

0

闭包无法从方法签名中推断生命周期。实际上,您正在定义一个生命周期在方法签名中,假设它是隐式的'a,并且另一个生命周期,在闭包中隐式地为'b

匹配这些生命周期,代码就可以编译了。

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

fn main() {
    foo("string");
}

非常抱歉如果我的问题表述不清楚,但我对闭包中生命周期推断的规则感兴趣,而不是如何使代码编译通过。此外,我尝试了一个带有静态字符串的版本:id("string"),但 Rust 仍然无法正确推断生命周期并产生相同的错误。如果 Rust 不支持闭包中的生命周期推断,我没有问题明确指定它,但错误消息提到了“冲突要求”,这让我感到非常困惑。 - svat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接