我需要一个闭包来引用其封闭环境中对象的某些部分。该对象是在环境中创建的,并且作用于它,但是一旦创建它就可以安全地移动到闭包。
用例是一个执行一些准备工作的函数,并返回一个将完成其余工作的闭包。这种设计的原因是执行约束:工作的第一部分涉及分配,其余部分必须不进行分配。这是一个最小的例子:
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
似乎没有帮助 - 编译器抱怨对象的活动时间不够长。
答案 0 :(得分:2)
这里的封闭没有什么特别之处;它相当于:
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,
}
}
fn main() {}
该对象是在环境中创建的,并且作用域为
我不同意; string
和substrings
在封闭环境的外部创建,移入其中。这就是让你绊倒的举动。
一旦创建,它就可以安全地移动到闭包。
在这个的情况下,这是真的,但只是因为你,程序员,可以保证里面的字符串数据的地址 {{1}将保持不变。你知道这有两个原因:
String
在内部使用堆分配实现,因此移动String
不会移动字符串数据。String
永远不会发生变异,这可能导致字符串重新分配,使任何引用无效。 您的示例的最简单的解决方案是将切片简单地转换为String
并让闭包完全拥有它们。如果这意味着你可以释放一个大字符串而不是一些较小的字符串,那么这甚至可能是一个净收益。
否则,您符合&#34下规定的标准;有一种特殊情况,即终身追踪过于热心&#34;在Why can't I store a value and a reference to that value in the same struct?中,您可以使用owning_ref:
String
请注意,我不是owning_ref的专家用户,因此这可能不是最佳使用它。