我的问题似乎与Rust error "cannot infer an appropriate lifetime for borrow expression" when attempting to mutate state inside a closure returning an Iterator密切相关,但我认为不一样。所以,这个
use std::iter;
fn example(text: String) -> impl Iterator<Item = Option<String>> {
let mut i = 0;
let mut chunk = None;
iter::from_fn(move || {
if i <= text.len() {
let p_chunk = chunk;
chunk = Some(&text[..i]);
i += 1;
Some(p_chunk.map(|s| String::from(s)))
} else {
None
}
})
}
fn main() {}
不编译。编译器说它无法确定 &text[..i]
的适当生命周期。这是我能想到的最小的例子。这个想法是,有一个内部状态,它是 text
的一个切片,迭代器返回从该内部状态分配的新字符串。我是 Rust 的新手,所以也许这很明显,但是我将如何注释生命周期以便编译?
请注意,此示例与链接示例不同,因为将 point
作为引用传递,而此处的 text
已移动。另外,这个答案现在已经有一年半的时间了,所以也许有更简单的方法。
编辑: 添加 p_chunk
以强调 chunk
需要在对 next
的调用之间保持持久性,因此不能是闭包的本地,但应该被捕获通过它。
答案 0 :(得分:1)
如果您将 chunk
Option
移动到闭包中,您的代码将被编译。我不能完全回答为什么在闭包外声明 chunk
会导致闭包内借用 text
的生命周期错误,但是 chunk
{{ 1}} 无论如何看起来都是多余的,以下代码应该是等效的:
Option
此外,您似乎不太可能真正需要 fn example(text: String) -> impl Iterator<Item = Option<String>> {
let mut i = 0;
iter::from_fn(move || {
if i <= text.len() {
let chunk = text[..i].to_string();
i += 1;
Some(Some(chunk))
} else {
None
}
})
}
而不是 Iterator<Item = Option<String>>
,因为迭代器无论如何都不会产生 Iterator<Item<String>>
。
Some(None)
注意,如果您将 fn example(text: String) -> impl Iterator<Item = String> {
let mut i = 0;
iter::from_fn(move || {
if i <= text.len() {
let chunk = text[..i].to_string();
i += 1;
Some(chunk)
} else {
None
}
})
}
作为参数并将输出的生命周期与输入联系起来,您也可以使用此迭代器而不为每个 String
分配 chunk
论点:
&str
答案 1 :(得分:1)
您的代码是尝试创建 self-referential struct 的示例,其中结构由闭包隐式创建。由于 text
和 chunk
都被移到了闭包中,因此您可以将它们视为结构体的成员。由于 chunk
引用了 text
中的内容,因此结果是一个自引用结构,当前借用检查器不支持该结构。
虽然自引用结构由于移动而通常是不安全的,但在这种情况下它是安全的,因为 text
是堆分配的并且随后不会发生变异,也不会逃脱闭包。因此,text
的内容不可能移动,一个足够聪明的借用检查器可以证明您正在尝试做的事情是安全的,并允许闭包编译。
[链接问题] 的答案表示可以通过 Option
进行引用,但之后无法移动结构。在我的例子中,自引用是在文本和块移动到位后创建的,并且它们永远不会再次移动,所以原则上它应该可以工作。
同意 - 原则上它应该可以工作,但众所周知,当前的借用检查器不支持它。支持需要多个新功能:借用检查器应该特殊情况的堆分配类型,如 Box
或 String
,其移动不会影响对其内容的引用,并且在这种情况下还证明您不要调整大小或mem::replace()
封闭的String
。
在这种情况下,最好的解决方法是“显而易见”的解决方法:不是持久化 chunk
切片,而是持久化一对 usize
索引(或 Range
)并创建切片当你需要的时候。