我正在尝试编写一个将 an 映射Arc<[T]>到 an的函数,以Iterable供使用flat_map(也就是说,我想调用i.flat_map(my_iter)其他一些i: Iterator<Item=Arc<[T]>>)。
fn my_iter<'a, T>(n: Arc<[T]>) -> slice::Iter<'a, T> {
let t: &'a [T] = &*n.clone();
t.into_iter()
}
Run Code Online (Sandbox Code Playgroud)
上面的函数不起作用,因为n.clone()产生了一个拥有的 type 值Arc<[T]>,我可以取消引用[T]然后借用 get &[T],但是借用的生命周期只持续到函数结束,而'a生命周期持续到客户端删除返回的迭代器。
如何Arc以客户端拥有克隆的所有权的方式克隆 ,以便仅在客户端使用迭代器完成后才删除该值(假设没有其他人使用Arc)?
下面是源迭代器的一些示例代码:
struct BaseIter<T>(Arc<[T]>);
impl<T> Iterator for BaseIter<T> {
type Item = Arc<[T]>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.0.clone())
}
}
Run Code Online (Sandbox Code Playgroud)
鉴于正在生成数据,而不仅仅是借用它,我如何实现BaseIter(data).flat_map(my_iter)(类型为Iterator<&T>)的结果?(实际情况比这更复杂,结果并不总是相同,但所有权语义是相同的。)BaseIter
你不可以做这个。请记住,Rust 中的生命周期纯粹是编译时实体,仅用于验证您的代码不会意外访问删除的数据。例如:
fn my_iter<'a, T>(n: Arc<[T]>) -> slice::Iter<'a, T>
Run Code Online (Sandbox Code Playgroud)
这里'a不会“持续到客户端删除返回的迭代器”;这种推理是不正确的。从slice::Iter它的生命周期参数来看,它指的是它所指向的切片的生命周期;从的角度来看my_iter 'a只是一个生命周期参数,可以由调用者任意选择。换句话说,slice::Iter总是依赖于一些切片用一些具体的寿命,但签名my_iter状态,它能够返回任意的寿命。你看到矛盾了吗?
作为旁注,由于生命周期的协方差,您可以从这样的函数返回静态切片的切片:
static DATA: &'static [u8] = &[1, 2, 3];
fn get_data<'a>() -> &'a [u8] {
DATA
}
Run Code Online (Sandbox Code Playgroud)
上面的定义是可以编译的,但它之所以有效DATA,是因为它存储在程序的静态内存中,并且在程序运行时始终有效;这不是这样的Arc<[T]>。
Arc<[T]>意味着共享所有权,即里面的数据Arc<[T]>是由原始Arc<[T]>值的所有克隆共同拥有的。因此,当 an 的最后一个克隆Arc超出范围时,它包含的值将被删除,并释放相应的内存。现在,考虑一下如果my_iter()允许编译会发生什么:
let iter = {
let data: Arc<[i32]> = get_arc_slice();
my_iter(data.clone())
};
iter.map(|x| x+1).collect::<Vec<_>>();
Run Code Online (Sandbox Code Playgroud)
因为 inmy_iter() 'a可以是任意的,并且没有以任何方式链接到Arc<[T]>(实际上也不能),没有什么可以阻止这段代码编译 - 用户也可以选择'static生命周期。然而,这里所有的克隆都data将被放入块中,并且它包含在里面的数组将被释放。iter在块之后使用是不安全的,因为它现在提供对已释放内存的访问。
如何以客户端拥有克隆的所有权的方式克隆 Arc,以便仅在客户端使用迭代器完成后才删除该值(假设没有其他人在使用 Arc)?
所以,从上面可以看出,这是不可能的。只有数据的所有者才能决定何时销毁该数据,而借用引用(其存在总是由生命周期参数隐含)可能只借用数据存在的时间,但借用不会影响数据何时以及如何销毁. 为了编译借用的引用,他们需要始终只借用在这些引用处于活动状态期间有效的数据。
你能做的是重新思考你的架构。如果不查看完整代码,很难说到底可以做什么,但是在这个特定示例的情况下,例如,您可以首先将迭代器收集到一个向量中,然后遍历该向量:
let items: Vec<_> = your_iter.collect();
items.iter().flat_map(my_iter)
Run Code Online (Sandbox Code Playgroud)
请注意, nowmy_iter()确实应该接受&Arc<[T]>,正如 Francis Gagné 所建议的那样;这样,输出迭代器的生命周期将与输入引用的生命周期相关联,一切都应该正常工作,因为现在可以保证Arcs 稳定地存储在向量中,以便以后在迭代期间仔细阅读。