Cannot infer an appropriate lifetime for autoref due to conflicting requirements

I know questions regarding lifetime have been asked before and I've read some of the threads to see if I can apply it to the issue I am experiencing. Unfortunately, probably due to inexperience, I'm unable to apply any of the suggestions in other threads to my specific problem.

I'm trying to get a list of all the files inside a zip archive using zip-rs. Relevant code (not sure if it matters but this is in a #[wasm_bindgen] fn):

    let buffer = &decode(contents.as_str()).unwrap();
    let reader = std::io::Cursor::new(buffer);

    let archive: ZipArchive<_> = ZipArchive::new(reader).unwrap();

    let files: Vec<ZipResult<ZipFile>> = (0..archive.len())
        .map(|i| archive.by_index(i))
        .collect();

It raises the following error (underlining the by_index call):

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/lib.rs:79:26
   |
79 |         .map(|i| archive.by_index(i))
   |                          ^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime '_ as defined on the body at 79:14...
  --> src/lib.rs:79:14
   |
79 |         .map(|i| archive.by_index(i))
   |              ^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `archive`
  --> src/lib.rs:79:18
   |
79 |         .map(|i| archive.by_index(i))
   |                  ^^^^^^^
note: but, the lifetime must be valid for the method call at 78:42...
  --> src/lib.rs:78:42
   |
78 |       let files: Vec<ZipResult<ZipFile>> = (0..archive.len())
   |  __________________________________________^
79 | |         .map(|i| archive.by_index(i))
   | |_____________________________________^
note: ...so that a type/lifetime parameter is in scope here
  --> src/lib.rs:78:42
   |
78 |       let files: Vec<ZipResult<ZipFile>> = (0..archive.len())
   |  __________________________________________^
79 | |         .map(|i| archive.by_index(i))
   | |_____________________________________^

If I understand this correctly by_index has a lifetime parameter tying the lifetime of ZipFile to the body of the map closure. So the compiler is saying, this type is only valid inside the closure but you want to collect it outside, that's not happening.

I think using an imperative approach (push inside a loop) would solve the issue since there would not be the "inner" scope.

Thanks @leudz. That was actually my first attempt (because that's what the document does). It gave me a different error though which led me to trying the map:

    let mut files: Vec<ZipResult<ZipFile>> = vec!();
    for i in 0..archive.len() {
        let file = archive.by_index(i);
        files.push(file);
    }

error:

error[E0499]: cannot borrow `archive` as mutable more than once at a time
  --> src/lib.rs:80:20
   |
80 |         let file = archive.by_index(i);
   |                    ^^^^^^^ mutable borrow starts here in previous iteration of loop

Given that I'm curious about 1) is there a way to tell closure that ZipFile should live longer and 2) is there a way to fix the mutable borrow error.

As for fixing the borrow error I tried cloning archive in various ways which either resulted in the same error but on the cloned variable. Or another lifetime error which I suspect is similar to the closure error:

error[E0597]: `x` does not live long enough
   --> src/lib.rs:81:20
    |
81  |         let file = x.by_index(i);
    |                    ^ borrowed value does not live long enough
82  |         files.push(file);
83  |     }
    |     - `x` dropped here while still borrowed
...
110 | }
    | - borrow might be used here, when `files` is dropped and runs the `Drop` code for type `std::vec::Vec`
    |
    = note: values in a scope are dropped in the opposite order they are defined

Oh, since it's in the read module I assumed it would borrow immutably.
So your code can't work, there can only be one ZipFile at a time. Are you sure you can't move the stuff you do with the Vec inside the for loop?

Well... you could in theory clone the archive for each index but that's silly, except if the archive is quite small.

So what I’m really trying to is get to the contents of one specific file and parse it as JSON.

There is actually an open PR for finding a specific file, but it looks like it won’t be merged anytime soon.

I guess technically I could do the work in the for loop but that seems wrong.

Wouldn't it be better inside the loop? You wouldn't have to iterate the whole archive, just until you find the right one. Then you parse it and you're done, the lifetime won't be an issue anymore.

1 Like