我有这个“简化”的代码来演示我在一个更复杂的项目中遇到的问题。
我创建了一个闭包来捕获一些参数,这样我就可以在两个地方调用一个小函数,而无需重复代码。不幸的是,现在涉及到生命周期,我很难理解编译器到底混淆了什么:
use std::io::Write;
pub struct X<'c>
{
maybe_file: Option<Box<dyn Write+'c>>,
}
impl<'c> X<'c>
{
pub fn wrap<'a:'c,'b:'c> (prefix:&'a str, base: &'b mut X<'b>) ->X<'c>
{
return X::<'c> {
maybe_file: Some(Box::new(X::wrapper(prefix, base))),
}
}
pub fn wrapper<'a, 'b>(prefix:&'a str, base:&'b mut X<'b>) -> Wrapper<'a,'b>
{
Wrapper {
prefix:prefix, base:base
}
}
pub fn boop_the_snoot(&self) {}
}
//
pub struct Wrapper<'a,'b>
{
pub prefix: &'a str,
pub base: &'b X<'b>,
}
impl<'a,'b> Write for Wrapper<'a,'b>
{
fn write(&mut self, buf:&[u8]) ->Result<usize, std::io::Error> { Ok(0) }
fn flush(&mut self) ->Result<(), std::io::Error> { Ok(()) }
}
pub fn bacon(x:&mut X, scale:f32)
{
}
pub fn eggs<'c>(x:&'c mut X<'c>, scale:f32)
{
bacon( & mut X::wrap("A:\t", x), scale);
let f = |x:& mut X| {
bacon(& mut X::wrap("B:\t", x), scale);
};
f(x);
f(x);
}
这会导致以下编译错误:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements
--> /home/thoth/src/embroidery/filler/src/lifetimes_shenanigans.rs:68:19
|
68 | bacon(& mut X::wrap("B:\t", x), scale);
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 67:13...
--> /home/thoth/src/embroidery/filler/src/lifetimes_shenanigans.rs:67:13
|
67 | let f = |x:&mut X| {
| _____________^
68 | | bacon(& mut X::wrap("B:\t", x), scale);
69 | | };
| |_____^
note: ...so that reference does not outlive borrowed content
--> /home/thoth/src/embroidery/filler/src/lifetimes_shenanigans.rs:68:35
|
68 | bacon(& mut X::wrap("B:\t", x), scale);
| ^
note: but, the lifetime must be valid for the anonymous lifetime #2 defined on the body at 67:13...
--> /home/thoth/src/embroidery/filler/src/lifetimes_shenanigans.rs:67:13
|
67 | let f = |x:&mut X| {
| _____________^
68 | | bacon(& mut X::wrap("B:\t", x), scale);
69 | | };
| |_____^
= note: ...so that the expression is assignable:
expected &mut lifetimes_shenanigans::X<'_>
found &mut lifetimes_shenanigans::X<'_>
这是什么样的终身犯罪的逻辑保护我?
为了使rust编译器能够理解各种对象的生命周期,我应该在代码中添加什么?
最佳答案
我可以通过用&'b mut X<'b>
替换&'z mut X<'b>
格式的声明来编译代码,基本上是将引用的生存期与X中字段的生存期分离。
另一个重要的变化是从eggs()
函数中删除所有生命周期。这实际上提出了它自己的问题:是否可以在eggs函数中明确声明生命周期,然后仍然让它编译?我做了一些笨拙的尝试,最后得到了[E0502]: cannot borrow
*xas immutable because it is also borrowed as mutable
这证实了我还不明白其中的微妙之处。
补丁如下所示:
@@ -7,13 +7,13 @@
impl<'c> X<'c>
{
- pub fn wrap<'a:'c,'b:'c> (prefix:&'a str, base: &'b mut X<'b>) ->X<'c>
+ pub fn wrap<'a:'c,'b:'c,'z:'c> (prefix:&'a str, base: &'z mut X<'b>) ->X<'c>
{
return X::<'c> {
maybe_file: Some(Box::new(X::wrapper(prefix, base))),
}
}
- pub fn wrapper<'a, 'b>(prefix:&'a str, base:&'b mut X<'b>) -> Wrapper<'a,'b>
+ pub fn wrapper<'a, 'b, 'z>(prefix:&'a str, base:&'z mut X<'b>) -> Wrapper<'a,'b, 'z>
{
Wrapper {
prefix:prefix, base:base
@@ -25,13 +25,13 @@
//
-pub struct Wrapper<'a,'b>
+pub struct Wrapper<'a,'b, 'z>
{
pub prefix: &'a str,
- pub base: &'b X<'b>,
+ pub base: &'z X<'b>,
}
-impl<'a,'b> Write for Wrapper<'a,'b>
+impl<'a,'b,'z> Write for Wrapper<'a,'b,'z>
{
fn write(&mut self, buf:&[u8]) ->Result<usize, std::io::Error> { Ok(0) }
fn flush(&mut self) ->Result<(), std::io::Error> { Ok(()) }
@@ -43,7 +43,7 @@
}
-pub fn eggs<'c>(x:&'c mut X<'c>, scale:f32)
+pub fn eggs(x:& mut X, scale:f32)
{
bacon( & mut X::wrap("A:\t", x), scale);
@@ -53,5 +53,7 @@
f(x);
+ x.boop_the_snoot();
+
f(x);
}
完整的“固定”源代码是
use std::io::Write;
pub struct X<'c>
{
maybe_file: Option<Box<dyn Write+'c>>,
}
impl<'c> X<'c>
{
pub fn wrap<'a:'c,'b:'c,'z:'c> (prefix:&'a str, base: &'z mut X<'b>) ->X<'c>
{
return X::<'c> {
maybe_file: Some(Box::new(X::wrapper(prefix, base))),
}
}
pub fn wrapper<'a, 'b, 'z>(prefix:&'a str, base:&'z mut X<'b>) -> Wrapper<'a,'b, 'z>
{
Wrapper {
prefix:prefix, base:base
}
}
pub fn boop_the_snoot(&self) {}
}
//
pub struct Wrapper<'a,'b, 'z>
{
pub prefix: &'a str,
pub base: &'z X<'b>,
}
impl<'a,'b,'z> Write for Wrapper<'a,'b,'z>
{
fn write(&mut self, buf:&[u8]) ->Result<usize, std::io::Error> { Ok(0) }
fn flush(&mut self) ->Result<(), std::io::Error> { Ok(()) }
}
pub fn bacon(x:&mut X, scale:f32)
{
}
pub fn eggs(x:& mut X, scale:f32)
{
bacon( & mut X::wrap("A:\t", x), scale);
let f = |x:&mut X| {
bacon(& mut X::wrap("B:\t", x), scale);
};
f(x);
x.boop_the_snoot();
f(x);
}
所以,这基本上是半个答案:代码编译后,我可以使用此模式继续我的项目;但是它依赖于rust编译器在编译eggs()时进行一些终生推断。如果我们用符合编译器推断的生存期信息来注释eggs(),代码会是什么样子?答案是教育性的。
关于rust - 我如何解决导致错误的闭包[E0495]:无法推断适当的生存期,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57912253/