将结构的迭代器传递给接受Rust中引用的函数?

时间:2016-03-01 22:41:45

标签: iterator rust

我有一个函数,它使用迭代器而不是对结构的引用。有时我正在迭代一个向量,它工作正常,但有时我创建一个迭代器,产生新的结构,我很难找到一个。当我在闭包中创建一个值时,我会得到它,当闭包时它会消失。当我不想要它时,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()的反义词)。这是什么方法?

2 个答案:

答案 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的函数支持。在许多情况下,甚至可能无法使用流式迭代器,因为算法需要一次引用多个值的自由。