我有一个函数,它使用迭代器而不是对结构的引用。有时我正在迭代一个向量,它工作正常,但有时我创建一个迭代器,产生新的结构,我很难找到一个。当我在闭包中创建一个值时,我会得到它,当闭包时它会消失。当我不想要它时,Rust总是试图将价值从事物中移出;为什么不在这里?
struct Thing {
value: u32
}
fn consume<'a, I: IntoIterator<Item=&'a Thing>>(things: I) {
for thing in things {
println!("{}", thing.value);
}
}
fn main () {
let solid = vec![Thing { value: 0 }];
let ephemeral = (1..5).map(|i| &Thing { value: i }); // Boxing Thing does not work either
consume(solid.iter());
consume(ephemeral);
}
但是
error: borrowed value does not live long enough
我有一种感觉,我需要将结构移出闭包和迭代器,或将其存储在某处。但Box
结构不起作用并返回结构而不是指针不会键入检查(我找不到.cloned()
的反义词)。这是什么方法?
答案 0 :(得分:5)
简短回答:你不能。
更长的解释:
这是“产生新结构的迭代器”:
let iterator_of_structs = (1..5).map(|i| Thing { value: i });
解决这个问题的主要技巧是始终询问“谁拥有数据?”。
每次调用next
时,闭包都会获得一个整数的所有权(通过i
)并构造一个新的Thing
。闭包返回Thing
,将所有权转移到调用next
的代码。
当你借用一个值(a.k.a.参考)时,价值的所有权不能转手,价值必须持续超过借款持续时间。
让我们转向引用迭代器的概念,并问我们的问题:“谁拥有数据?”。
map(|i| &Thing { value: i })
在这里,我们创建一个Thing
并引用它。没有变量拥有Thing
,因此范围拥有它,并且当范围结束时,该值将被销毁。封闭试图返回引用,但这违反了借用物品必须比借来的寿命更长的公理。
那么,你如何解决它?最简单的方法是将您的功能更改为更接受:
use std::borrow::Borrow;
struct Thing {
value: u32
}
fn consume<'a, I>(things: I)
where I: IntoIterator,
I::Item: Borrow<Thing>,
{
for thing in things {
let thing = thing.borrow();
println!("{}", thing.value);
}
}
fn main () {
let iterator_of_structs = (1..5).map(|i| Thing { value: i });
consume(iterator_of_structs);
let vector_of_structs: Vec<_> = (1..5).map(|i| Thing { value: i }).collect();
let iterator_of_references_to_structs = vector_of_structs.iter();
consume(iterator_of_references_to_structs);
}
在这里,我们接受任何允许我们借用对Thing
的引用的项的迭代器。这适用于任何项目和对项目的任何引用。
答案 1 :(得分:3)
引用的迭代器允许使用者保留迭代器所产生的所有引用,只要它们需要(至少在迭代器本身保持活动状态时)。显然,为了支持这一点,迭代器创建引用的所有对象需要同时在内存中。使用迭代器协议无法解决这个问题。所以你最好的做法是将collect()
迭代器放到一个向量中并从中创建一个引用迭代器(和solid
一样)。不幸的是,这意味着失去了懒惰。
有一个替代的迭代器抽象,称为流迭代器,它将支持这一点。使用流式迭代器,消费者可能只保留引用,直到获得下一个。我不知道有任何条件实现这一点,并且它将是一个完全不同的特性,没有使用std::iter::Iterator
的函数支持。在许多情况下,甚至可能无法使用流式迭代器,因为算法需要一次引用多个值的自由。