2016-03-23 55 views
2

返回借我试图写一个Arc<[T]>映射到一个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() 
} 

功能上面不起作用,因为n.clone()产生Arc<[T]>型的资价值,我可以取消引用到[T]再借用得到&[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()) 
    } 
} 

如何实现的BaseIter(data).flat_map(my_iter)的结果(这是类型Iterator<&T>的)鉴于BaseIter生产数据,不只是借用它? (真实情况比这更复杂,并不总是相同的结果,但所有权语义是相同的。)

回答

4

你不能做到这一点。请记住,Rust中的生存期纯粹是编译时实体,仅用于验证您的代码是否意外地访问了丢弃的数据。例如:

fn my_iter<'a, T>(n: Arc<[T]>) -> slice::Iter<'a, T> 

这里'a没有“最后,直到客户端下降到返回的迭代器”;这个推理是不正确的。从slice::Iter的角度来看,其寿命参数意味着它指向的切片的寿命;从my_itermy_iter'a的角度来看只是一个终身参数,可以由调用者任意选择。换句话说,slice::Iter总是与一些切片有关,其中一些具体的生存期,但my_iter的签名表明它能够返回任意生存期。你看到矛盾了吗?

作为一个侧面说明,由于寿命的协方差可以从这样一个函数返回一个静态片段的片段:

static DATA: &'static [u8] = &[1, 2, 3]; 

fn get_data<'a>() -> &'a [u8] { 
    DATA 
} 

上述定义编译,但它仅适用,因为DATA存储在你的程序的静态内存中,并且是总是当你的程序运行时有效; Arc<[T]>并非如此。

Arc<[T]>意味着共享所有权,就是里面Arc<[T]>数据联合原Arc<[T]>值的所有克隆所有。因此,当Arc的最后一个克隆超出作用域时,它所包含的值将被删除,并释放相应的内存。现在,考虑是否my_iter()被允许编译会发生什么:

let iter = { 
    let data: Arc<[i32]> = get_arc_slice(); 
    my_iter(data.clone()) 
}; 
iter.map(|x| x+1).collect::<Vec<_>>(); 

由于my_iter()'a可任意以任何方式不挂Arc<[T]>(也不可能是,实际上),没有什么可以阻止从这个代码编译 - 用户不妨选择'static一生。但是,这里所有data的克隆都将被放入块中,并且其中包含的数组将被释放。在块不安全之后使用iter,因为它现在提供对释放内存的访问。

如何克隆Arc以便客户端获得克隆的所有权,以便在客户端完成迭代器后才会删除该值(假设没有其他人正在使用Arc) ?

所以,从以上的如下,这是不可能的。只有数据的所有者确定何时该数据应予以销毁,并借用引用(其存在总是由寿命参数暗示)只能借用数据时,它的存在时间,但借用无法影响何时以及如何数据被破坏。为了借用引用进行编译,他们需要始终只借用在这些引用处于活动状态的整个时间内有效的数据。

你可以做的是重新考虑你的架构。如果没有查看完整的代码,很难说完全可以做什么,但是在这个特定示例的情况下,例如,您可以首先将迭代器收集到矢量中,然后遍历矢量:

let items: Vec<_> = your_iter.collect(); 
items.iter().flat_map(my_iter) 

请注意,现在my_iter()确实应该接受&Arc<[T]>,正如弗朗西斯加涅所指出的那样;这样,输出迭代器的生命周期将与输入引用的生命周期相关联,并且一切都应该正常工作,因为现在可以确保将Arcs稳定地存储在向量中,以便在迭代过程中进行后续细读。

+0

感谢这个启发性的写作,这帮助我解决了这个问题。在这种情况下,我意识到我过度使用'Arc';迭代器不应该使用'Arc',因为它们不拥有数据的所有权 - 客户端已经拥有'data'字段,迭代器应该从中借用。 (我的数据结构是一种具有共享子项的二叉树,所以我认为'Arc'仍然适用于数据结构中的子链接,但是迭代器不应该控制数据。) –

+0

@MarioCarneiro,yep ,你的总结是正确的。只有当原始结构被消耗时,迭代器才拥有它们之后产生的数据的所有权,就像使用'Vec :: into_iter()'一样,否则引用更合适。 –

2

没有办法通过按价值传递Arc<[T]>来完成此项工作。您需要从对Arc<[T]>的引用开始,以构建有效的slice::Iter

fn my_iter<'a, T>(n: &'a Arc<[T]>) -> slice::Iter<'a, T> { 
    n.into_iter() 
} 

或者,如果我们的的Elid寿命:

fn my_iter<T>(n: &Arc<[T]>) -> slice::Iter<T> { 
    n.into_iter() 
} 
+1

嗯,这是一个问题。我必须使用'Arc <[T]>'作为输入,因为它来自'Iterator >'。我不能真正将它改为'Iterator >',因为源通过克隆来自不可变后备向量的子副本创建值,然后让它们依靠'Arc'清理后备向量当它用完引用。使用'&Arc <[T]>'这种方式似乎只是推动了这个问题。 –

+0

我为源迭代器添加了一些代码。你将如何修改它以产生'&Arc <[T]>'数据? –

0

您需要使用另一个迭代器的功能my_iter的返回类型。 slice::Iter<'a, T>有一个关联的类型Item = &'a T。您需要一个具有关联类型Item = T的迭代器。像vec::IntoIter<T>。你可以自己实现这样一个迭代器:

use std::sync::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()) 
    } 
} 

struct ArcIntoIter<T>(usize, Arc<[T]>); 

impl<T:Clone> Iterator for ArcIntoIter<T> { 
    type Item = T; 

    fn next(&mut self) -> Option<Self::Item> { 
     if self.0 < self.1.len(){ 
      let i = self.0; 
      self.0+=1; 
      Some(self.1[i].clone()) 
     }else{ 
      None 
     }  
    } 
} 

fn my_iter<T>(n: Arc<[T]>) -> ArcIntoIter<T> { 
    ArcIntoIter(0, n) 
} 

fn main() { 
    let data = Arc::new(["A","B","C"]); 
    println!("{:?}", BaseIter(data).take(3).flat_map(my_iter).collect::<String>()); 
    //output:"ABCABCABC" 
}