从自有资源返还借款

时间:2016-03-23 04:05:37

标签: rust

我正在尝试编写一个将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,以便仅在客户端完成迭代器之后删除该值(假设没有其他人使用{ {1}})?

这里是源迭代器的一些示例代码:

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>)的结果? (真实的东西比这更复杂,它并不总是相同的结果,但所有权语义是相同的。)

3 个答案:

答案 0 :(得分:4)

你不能这样做。请记住,Rust中的生命周期是纯粹的编译时实体,仅用于验证您的代码是否不会意外访问已删除的数据。例如:

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

这里'a不会“持续到客户端丢弃返回的迭代器”;这个推理是不正确的。从slice::Iter的角度来看,它的生命周期参数意味着它指向的切片的生命周期;从my_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]>,正如FrancisGagné所建议的那样;这样,输出迭代器的生命周期将与输入引用的生命周期相关联,并且一切都应该正常工作,因为现在可以保证Arc被稳定地存储在向量中,以便稍后在迭代。

答案 1 :(得分:2)

通过传递Arc<[T]>值,无法完成这项工作。您需要从对Arc<[T]>的引用开始,才能构建有效的slice::Iter

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

或者,如果我们忽视了一生:

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

答案 2 :(得分: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"
}