Michele De Pascalis
Michele De Pascalis

Reputation: 952

Cannot understand where a reference to a local value is being returned in Rust

I'm struggling to understand where I'm returning a reference to a local value in this function (full code: https://gist.github.com/9f88f9ded8f2f6a1f3b839422a521073):

fn encode_initial_configs<'a, TSym, NTSym>(
    alpha: impl IntoIterator<Item = TSym> + 'a,
    grammar: &'a OPGrammar<TSym, NTSym>,
    chunk_size: usize,
) -> impl Iterator<Item = GPUParseConfig> + 'a
where
    TSym: Eq + std::hash::Hash + Clone,
    NTSym: Eq + Clone,
{
    assert_ne!(chunk_size, 0, "`chunk_size` must be non-zero.");

    let mut alpha_chunks = grammar
        .encode_iterator(alpha)
        .chunks(chunk_size)
        .into_iter()
        .map(|chunk| chunk.collect_vec())
        .fuse();
    let curr_chunk = alpha_chunks.next();
    InitialConfigs {
        alpha_chunks,
        last_sym: 0,
        curr_chunk,
    }
}

The compiler complains about the value returned in the bottom:

error[E0515]: cannot return value referencing temporary value
  --> src/par_parse.rs:77:5
   |
70 |       let mut alpha_chunks = grammar
   |  ____________________________-
71 | |         .encode_iterator(alpha)
72 | |         .chunks(chunk_size)
   | |___________________________- temporary value created here
...
77 | /     InitialConfigs {
78 | |         alpha_chunks,
79 | |         last_sym: 0,
80 | |         curr_chunk,
81 | |     }
   | |_____^ returns a value referencing data owned by the current function
   |
   = help: use `.collect()` to allocate the iterator

For more information about this error, try `rustc --explain E0515`.

But the alpha_chunks iterator returned as part of the InitialConfigs instance is obtained by moving every intermediate iterator into the next one. Where is this reference?

EDIT: encode_iterator might be relevant:

    pub fn encode_iterator<'a>(
        &'a self,
        s: impl IntoIterator<Item = TSym> + 'a,
    ) -> impl Iterator<Item = u32> + 'a {
        s.into_iter().map(|sym| self.encode_term(sym))
    }

Upvotes: 0

Views: 106

Answers (2)

Michele De Pascalis
Michele De Pascalis

Reputation: 952

The culprit seems to be .into_iter(), which instead of consuming the result of .chunks(chunk_size), borrows it. In fact, reading the itertools code, IntoIterator is impl'd for &'a IntoChunks<I>, which is the type of the result of chunks. Furthermore, the borrowed reference to IntoChunks is stored in the returned Chunks struct, which is then stored into the iterator returned by map, and so on. A possible solution could be finding or implementing a chunk iterator that can be moved directly into map and does not hold references to local values.

Upvotes: 1

Sudip Ghimire
Sudip Ghimire

Reputation: 715

Short answer: You are returning reference to alpha chunk

What's happening?

Let's look at this statement:

let mut alpha_chunks = grammar
        .encode_iterator(alpha)
        .chunks(chunk_size)
        .into_iter()
        .map(|chunk| chunk.collect_vec())
        .fuse();

Here are the diffusion of statement:

  • grammer refer to the variable passed into the function itself
  • endoe_iterator() returns an iterator which which yields the item that is passed as argument. But note that iterator is local in itself, it just yields value that is not local
  • chunks(), into_iterator(), map(), fuse() all this just manipulate the iterator and keep re-making new iterators type
  • final iterator of above statement will be stored in alpha_chunks variable which is also local

So you realize that iterators are lazy i.e they don't compute anything unless called with come methods that actually make them to like collect() method. But in you case you never "collected" anything from iterator and the function just return the iterator itself which is local

Upvotes: 0

Related Questions