gpt4 book ai didi

rust - 闭包中引用的生命周期

转载 作者:行者123 更新时间:2023-11-29 08:07:48 25 4
gpt4 key购买 nike

我需要一个闭包来引用其封闭环境中的对象的各个部分。该对象在环境中创建并限定在它的范围内,但一旦创建它就可以安全地移动到闭包中。

用例是一个函数,它执行一些准备工作并返回一个将完成其余工作的闭包。这种设计的原因是执行约束:工作的第一部分涉及分配,其余部分不得分配。这是一个最小的例子:

fn stage_action() -> Box<Fn() -> ()> {
// split a freshly allocated string into pieces
let string = String::from("a:b:c");
let substrings = vec![&string[0..1], &string[2..3], &string[4..5]];

// the returned closure refers to the subtrings vector of
// slices without any further allocation or modification
Box::new(move || {
for sub in substrings.iter() {
println!("{}", sub);
}
})
}

fn main() {
let action = stage_action();
// ...executed some time later:
action();
}

编译失败,正确说明 &string[0..1] 和其他的不能比 string 长。但是如果 string 被移动到闭包中,就不会有问题。有没有一种方法可以强制这种情况发生,或者有另一种方法可以让闭包引用在闭包之外创建的对象的各个部分?

我还尝试创建一个具有相同功能的struct 来使移动完全明确,但是doesn't compile either .同样,编译失败,错误是 &later[0..1] 和其他人只活到函数结束,但“借用的值必须在静态生命周期内有效”。

甚至completely avoiding a Box似乎没有帮助 - 编译器提示该对象的生命周期不够长。

最佳答案

这里没有什么特定于闭包的;它相当于:

fn main() {
let string = String::from("a:b:c");
let substrings = vec![&string[0..1], &string[2..3], &string[4..5]];
let string = string;
}

您正试图在有未完成的借用时移动 String。在我这里的例子中,它是另一个变量;在你的例子中它是闭包的环境。无论哪种方式,您仍在移动它。

此外,您正在尝试将子字符串移动到与拥有字符串相同的闭包环境中。这使得整个问题等同于 Why can't I store a value and a reference to that value in the same struct? :

struct Environment<'a> {
string: String,
substrings: Vec<&'a str>,
}

fn thing<'a>() -> Environment<'a> {
let string = String::from("a:b:c");
let substrings = vec![&string[0..1], &string[2..3], &string[4..5]];
Environment {
string: string,
substrings: substrings,
}
}

The object is created within the environment and is scoped to it

我不同意; stringsubstrings 在闭包环境的外部 创建并移入其中。正是这个举动让你绊倒了。

once created it could be safely moved to the closure.

这种情况下是正确的,但这只是因为您,程序员,可以保证 String 中的字符串数据的地址将保持不变。您知道这一点有两个原因:

  • String 在内部通过堆分配实现,因此移动 String 不会移动字符串数据。
  • String 永远不会发生突变,这可能会导致字符串重新分配,从而使任何引用无效。

对于您的示例,最简单 解决方案是简单地将切片转换为 String 并让闭包完全拥有它们。如果这意味着您可以释放一个大字符串以支持几个较小的字符串,这甚至可能是一个净 yield 。

否则,您符合 Why can't I store a value and a reference to that value in the same struct? 中“生命周期跟踪过度的特殊情况”下列出的标准。 , 所以你可以使用像这样的 crate :

owning_ref

use owning_ref::RcRef; // 0.4.1
use std::rc::Rc;

fn stage_action() -> impl Fn() {
let string = RcRef::new(Rc::new(String::from("a:b:c")));

let substrings = vec![
string.clone().map(|s| &s[0..1]),
string.clone().map(|s| &s[2..3]),
string.clone().map(|s| &s[4..5]),
];

move || {
for sub in &substrings {
println!("{}", &**sub);
}
}
}

fn main() {
let action = stage_action();
action();
}

ouroboros

use ouroboros::self_referencing; // 0.2.3

fn stage_action() -> impl Fn() {
#[self_referencing]
struct Thing {
string: String,
#[borrows(string)]
substrings: Vec<&'this str>,
}

let thing = ThingBuilder {
string: String::from("a:b:c"),
substrings_builder: |s| vec![&s[0..1], &s[2..3], &s[4..5]],
}
.build();

move || {
thing.with_substrings(|substrings| {
for sub in substrings {
println!("{}", sub);
}
})
}
}

fn main() {
let action = stage_action();
action();
}

请注意,我不是这两个 crate 的专家用户,因此这些示例可能不是它们的最佳用途。

关于rust - 闭包中引用的生命周期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42747620/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com