0

I am writing a program in Rust that reads data from excel sheet and populates objects using that data. To convert each row of excel sheet into a Test object, I implemted From<std::slice::Iter<'_, calamine::DataType>> for Test. My code looks like below.

use calamine::{open_workbook, DataType, Reader, Xlsx};
use std::convert::From;
use std::path::Path;
use std::slice::Iter;

#[derive(Debug)]
pub struct Test {
    pub field1: String,
    pub field2: String,
    pub field3: String,
}

impl From<Iter<'_, calamine::DataType>> for Test {
    fn from(mut val: Iter<'_, calamine::DataType>) -> Self {
        Test {
            field1: val.next().expect("failed").to_string(),
            field2: val.next().expect("failed").to_string(),
            field3: val.next().expect("failed").to_string(),
        }
    }
}

fn read_from_excel_sheet<'a, 'b, T: From<Iter<'a, calamine::DataType>>, P: AsRef<Path>>(
    wb_path: P,
    ws_name: &'b str,
) {
    let mut workbook: Xlsx<_> = open_workbook(wb_path).expect("Cannot open wb");

    if let Some(Ok(range)) = workbook.worksheet_range(ws_name) {
        for row in range.rows().skip(1) {
            let new_item = T::from(row.iter());
        }
    }
}

When I complied it, I got an error.

error[E0597]: `range` does not live long enough
  --> src/main.rs:31:20
   |
25 | fn read_from_excel_sheet<'a,'b, T: From<Iter<'a, calamine::DataType>>, P: AsRef<Path>>(wb_path: P, ws_name: &'b str) {
   |                          -- lifetime `'a` defined here
...
29 |     if let Some(Ok(range)) = workbook.worksheet_range(ws_name) {
   |                    ----- binding `range` declared here
30 |
31 |         for row in range.rows().skip(1) {
   |                    ^^^^^^^^^^^^ borrowed value does not live long enough
32 |
33 |             let new_item = T::from(row.iter());
   |                            ------------------- argument requires that `range` is borrowed for `'a`
...
36 |     }
   |     - `range` dropped here while still borrowed

I can understand what the error message is saying but don't know why the lifetime of range matters. As the message points out, range will be dropped when the program reaches the end of if block, but at that time nothing references it because new_item is created and dropped in each iteration and each field of Test is owned type(String), which means it does not depend on anything.

After serveral tries, I found a soultion, but I still don't know why it works.

fn read_from_excel_sheet<'b, T: for<'a> From<Iter<'a, calamine::DataType>>, P: AsRef<Path>>(
    wb_path: P,
    ws_name: &'b str,
) {
    let mut workbook: Xlsx<_> = open_workbook(wb_path).expect("Cannot open wb");

    if let Some(Ok(range)) = workbook.worksheet_range(ws_name) {
        for row in range.rows().skip(1) {
            let new_item = T::from(row.iter());
        }
    }
}
cafce25
  • 15,907
  • 4
  • 25
  • 31
JUSEOK KO
  • 69
  • 7
  • 1
    With your first version the caller could call `read_from_excel_sheet::<'static, 'b, _, _>(wb_path, ws_name)` and you'd have to keep `range` around forever, but you don't do that so the compiler rejects it. Man those kind of questions are hard to find a duplicate for, though I'm sure I've come across one. – cafce25 Aug 09 '23 at 03:20
  • @cafce25 Do you mean that the compiler can decide a lifetime and because I did not pass a value from which the compiler can infer the lifetime for ‘a, it called the function with ‘static as a lifetime for ‘a? – JUSEOK KO Aug 09 '23 at 04:40
  • No, it doesn't matter what you actually call it with, but what you *can* call it with. The compiler has to produce code that works for every lifetime `'a` (and `'b` but that doesn't matter here) so the code has to be valid with any lifetime passed in, the first variant isn't valid with every lifetime though in fact it's not valid with any lifetime from outside since all of them outlive the function call, but `range` doesn't live long enough for that. – cafce25 Aug 09 '23 at 05:49
  • Not related to your actual question, but is there a reason that you are implementing `From` with the specific type `slice::Iter` rather than from any `Iterator`? – Kushagra Gupta Aug 10 '23 at 05:12
  • @KushagraGupta Yes, I did impermented ```From``` with ```Iterator<..>```, but as you can see from my code, to populate ```new_item``` I passed ```row.iter()``` which was a value with a specific type. So complier raised an error saying that I passed a value to where a type paramter should be passed. – JUSEOK KO Aug 10 '23 at 10:47

0 Answers0