1

I'm trying to iterate over a series of filenames and parse the corresponding files.

My Cargo.toml:

[package]
name = "fold"
version = "0.1.0"
authors = ["Akshat Mahajan"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
goblin = "0.4.3"

My main.rs:

/// A program linker to generate x64 ELF executables from object files.
use goblin::elf::Elf;
use std::error::Error;
use std::fs;

fn parse_files(files: Vec<String>) -> Result<Vec<Elf<'static>>, Box<dyn Error>> {
    let mut elfs: Vec<Elf> = vec![];
    for file in files {
        elfs.push(Elf::parse(fs::read(file)?.as_slice())?);
    }

    return Ok(elfs);
}

fn main() {
    parse_files(vec!["foo".to_string(), "bar".to_string()]);
}

The borrow checker complains:

error[E0515]: cannot return value referencing temporary value
  --> src/main.rs:17:12
   |
14 |         elfs.push(Elf::parse(fs::read(file)?.as_slice())?);
   |                              --------------- temporary value created here
...
17 |     return Ok(elfs);
   |            ^^^^^^^^ returns a value referencing data owned by the current function

Elf::parse only accepts a byte slice, rather than anything heap-allocated for a longer lifetime:

pub fn parse(bytes: &'a [u8]) -> Result<Self>

What is the correct way to make this operation succeed?

Things I've tried:

  1. Moving everything in parse_files into main, only to be hit by a drop of temporary variable error.
  2. Read Is there any way to return a reference to a variable created in a function?. I don't think this helps me because it seems my actual problem is that the lifetime of the byte stream created by fs::read is temporary, when I really want it to be tied to the lifetime of the program. I don't know how to achieve this cleanly.
TylerH
  • 20,799
  • 66
  • 75
  • 101
Akshat Mahajan
  • 9,543
  • 4
  • 35
  • 44
  • It looks like your question might be answered by the answers of [Is there any way to return a reference to a variable created in a function?](https://stackoverflow.com/q/32682876/155423). If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Dec 21 '21 at 18:13
  • *net helpful to answerers* — the smaller the reproduction, the easier it is for answerers to reproduce. The smaller the reproduction, the easier it is for other question askers to identify if the problem is one that they also have. – Shepmaster Dec 21 '21 at 18:50
  • I think this difference arose because, in other language tags (e.g. Python), producing full source (if <100 lines) is sufficient to meet both needs. Anyway, I shan't take up more time on your end on this :) Please assist if you can. – Akshat Mahajan Dec 21 '21 at 18:57
  • 1
    *I really want it to be tied to the lifetime of the program* — that's usually called a "global variable". See also [How do I create a global, mutable singleton?](https://stackoverflow.com/q/27791532/155423) – Shepmaster Dec 21 '21 at 19:01
  • 2
    *Moving everything in `parse_files` into `main`* — I'd expect that you could read all the files into one vector, then map over the vector, parsing the data, and creating another vector. Then the data would stay around the whole time. – Shepmaster Dec 21 '21 at 19:03
  • @Shepmaster I attempted to follow your directions for `read all the files into one vector...`, but the borrow checker still doesn't like me. I've created a new question for it, and will link - would appreciate your response on it: https://stackoverflow.com/questions/70440795/cannot-return-value-referencing-function-parameter-when-parsing-a-list-of-elfs-w – Akshat Mahajan Dec 21 '21 at 19:40

1 Answers1

0

After taking Shepmaster's suggestion into account, I rewrote the example above to be just:

use goblin::elf::Elf;
use std::error::Error;
use std::fs;

fn main() -> Result<(), Box<dyn Error>> {

    let files = vec!["foo".to_string(), "bar".to_string()];
    let elfs = files.iter().map(fs::read).collect::<Result<Vec<Vec<u8>>,_>>()?;
    let _parsed_elfs = elfs.iter().map(|x| Elf::parse(x.as_slice())).collect::<Result<Vec<Elf>, _>>();

    return Ok(());
}

This successfully passes the borrow checker.

Akshat Mahajan
  • 9,543
  • 4
  • 35
  • 44