[英]How do I work around closures causing error[E0495]: cannot infer an appropriate lifetime
I have this "simplified" code to demonstrate a problem I am having with a more complicated project. 我有这个“简化的”代码来演示我在一个更复杂的项目中遇到的问题。
I have created a closure to capture some parameters so I can invoke a small function in two places without repeating the code. 我创建了一个闭包以捕获一些参数,因此可以在两个地方调用一个小函数而无需重复代码。 Unfortunately, lifetimes are now involved, and I am having difficulty understanding what exactly has the compiler confused:
不幸的是,现在涉及生命周期,并且我很难理解编译器到底混淆了什么:
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);
}
This gives me the following compile error: 这给了我以下编译错误:
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<'_>
What sort of lifetime crimes is this logic protecting me from? 这种逻辑可以保护我免受一生的罪行吗? What should I add to this code to enable the rust compiler to understand the lifetimes of the various objects?
我应该在代码中添加些什么,以使rust编译器了解各种对象的生存期?
I was able to get the code to compile by replacing the declarations of the form &'b mut X<'b>
with &'z mut X<'b>
, basically decoupling the lifetime of the reference from the lifetime of the field inside X. 我可以通过将
&'b mut X<'b>
形式的声明替换为&'z mut X<'b>
声明要编译的代码,基本上将引用的生存期与字段内部的生存期解耦X。
Another important change was to remove all lifetimes from the eggs()
function. 另一个重要的更改是删除了
eggs()
函数的所有生命周期。 This actually raises its own question: Is it possible to explcitly declare the lifetimes in the eggs function and still get it to compile? 这实际上提出了自己的问题:是否有可能在eggs函数中明确声明生命周期并仍然可以对其进行编译? I have made a few clumsy attempts and ended up with
[E0502]: cannot borrow
*x as immutable because it is also borrowed as mutable
which confirms there are subtleties I do not yet understand. 我做了一些笨拙的尝试,并以
[E0502]: cannot borrow
* x as immutable because it is also borrowed as mutable
[E0502]: cannot borrow
as immutable because it is also borrowed as mutable
从而证实了我尚不了解的微妙之处。
The patch looks like this: 该补丁看起来像这样:
@@ -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);
}
And the complete "fixed" source code is 完整的“固定”源代码是
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);
}
So, this is basically half an answer: the code compiles and I can continue with my project using this pattern; 因此,这基本上是一半的答案:代码可以编译,我可以使用这种模式继续我的项目; However it relies on the rust compiler to do some lifetime inferences while compiling eggs().
但是,它依赖rust编译器在编译egg()时进行一些生命周期推断。 What would the code look like if we were to annotate eggs() with lifetime information that matches what the compiler infers?
如果我们用与编译器推断的匹配的生存期信息注释egg(),代码将是什么样? The answer to that would be educational.
答案将是教育性的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.