1

I want to create and return my headers & statements from a function. The statements is a vector each element borrows a value from the headers like so:

use csv::{Reader, StringRecord};
use std::{collections::VecDeque, error::Error};

fn main() {
    let (headers, statements) = run().unwrap();
}

fn run<'a>() -> Result<(StringRecord, VecDeque<Statement<'a>>), Box<dyn Error>> {
    let mut rdr = Reader::from_path("input.csv")?;
    let headers = rdr.headers()?.clone();
    let mut statements = VecDeque::new();

    for result in rdr.records() {
        let record = result?;

        for (i, v) in record.iter().enumerate() {
            statements.push_back(Statement::new(&headers[i]));
        }
    }

    Ok((headers, statements))
}

struct Statement<'a> {
    label: &'a str,
}
impl<'a> Statement<'a> {
    fn new(label: &'a str) -> Self {
        Statement { label }
    }
}

There're errors with the above snippet, could you please explain why and show me the way to fix this?

error[E0515]: cannot return value referencing local variable `headers`
  --> src/main.rs:21:5
   |
17 |             statements.push_back(Statement::new(&headers[i]));
   |                                                  ------- `headers` is borrowed here
...
21 |     Ok((headers, statements))
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `headers` because it is borrowed
  --> src/main.rs:21:9
   |
8  | fn run<'a>() -> Result<(StringRecord, VecDeque<Statement<'a>>), Box<dyn Error>> {
   |        -- lifetime `'a` defined here
...
17 |             statements.push_back(Statement::new(&headers[i]));
   |                                                  ------- borrow of `headers` occurs here
...
21 |     Ok((headers, statements))
   |     ----^^^^^^^--------------
   |     |   |
   |     |   move out of `headers` occurs here
   |     returning this value requires that `headers` is borrowed for `'a`

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
warning: `rust` (bin "rust") generated 3 warnings
error: could not compile `rust` due to 2 previous errors; 3 warnings emitted
hnb
  • 107
  • 1
  • 6

2 Answers2

1

TL;DR

You dont return anything that is a reference created inside a function;

Instead: you can only return something that was passed as parameter to the function (with lifetimes);

Solution: use String instead of &'a str (its not really the solution, but its a convenient solution to your case).

Code fix

Here's a quick fix to your code. Instead of returning a reference, use String instead.

use csv::{Reader, StringRecord};
use std::{collections::VecDeque, error::Error};

fn main() {
    let (headers, statements) = run().unwrap();
}

fn run() -> Result<(StringRecord, VecDeque<Statement>), Box<dyn Error>> {
    let mut rdr = Reader::from_path("input.csv")?;
    let headers = rdr.headers()?.clone();
    let mut statements = VecDeque::new();

    for result in rdr.records() {
        let record = result?;

        for (i, v) in record.iter().enumerate() {
            statements.push_back(Statement::new(headers[i].to_owned()));
        }
    }

    Ok((headers, statements))
}

struct Statement {
    label: String,
}
impl Statement {
    fn new(label: String) -> Self {
        Statement { label }
    }
}

changes:

  • run fn header: removing the lifetime, not necessary anymore
- fn run<'a>() -> Result<(StringRecord, VecDeque<Statement<'a>>), Box<dyn Error>> {
+ fn run() -> Result<(StringRecord, VecDeque<Statement>), Box<dyn Error>> {
  • push_back part: converting the &str into String; note: you can also use .clone() here, because to_owned() is the same as clone()
- statements.push_back(Statement::new(&headers[i]));
+ statements.push_back(Statement::new(headers[i].to_owned()));
  • Statement struct: removing lifetime, adding String struct
- struct Statement<'a> {
+ struct Statement {
-     label: &'a str
+     label: String,
}
impl Statement {
-    fn new(label: &'a str) -> Self {
+    fn new(label: String) -> Self {
         Statement { label }
    }
}

I've ran the code, works like a charm.

Good luck in your program!

alexzander
  • 1,586
  • 1
  • 9
  • 21
-1

In this line

statements.push_back(Statement::new(&headers[i]));

The values of headers are borrowed because the Statement accesses the value with a reference.

You should copy or clone the headers[i] to solve the problem. Headers still hold ownership of their initial values. Statements get a copy of them.

Thai Anh Duc
  • 524
  • 6
  • 12