The idea is I have a Vec<&str>. I replace the first element with a string from other source, then I do join(",") on the Vec.
The compiler says that
... dropped here while still borrowed
I paste code here if you don't want to go outside:
extern crate chrono;
use chrono::{UTC, TimeZone};
fn process_line(line: String) -> Option<String> {
let mut columns: Vec<&str> = line.split(',').collect();
let timestamp = columns[0].parse::<i64>();
if timestamp.is_err() {
return None;
}
let datatime = UTC.timestamp(timestamp.unwrap(), 0).to_string();
columns[0] = datatime.as_str();
Some(columns.join(","))
}
fn main() {
// Convert the timestamp in first column to datetime string
let s = String::from("1489544029,20");
process_line(s).unwrap();
}
And here the compilation error:
warning: unused manifest key: package.published
Compiling ali v0.1.0 (file:///home/quan/Works/Test/Rust/ali)
error: `datatime` does not live long enough
--> src/main.rs:13:2
|
11 | columns[0] = datatime.as_str();
| -------- borrow occurs here
12 | Some(columns.join(","))
13 | }
| ^ `datatime` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
error: aborting due to previous error
error: Could not compile `ali`.
What happens is that you are storing datatime.as_str() into columns. As datatime is owned string, and it's deallocated before columns, you would end up with a state where columns contains an invalid pointer after datatime is deallocated, but columns isn't.
The solution is to move declaration of datatime to be deallocated after columns.
fn process_line(line: String) -> Option<String> {
let datatime;
let mut columns: Vec<&str> = line.split(',').collect();
let timestamp = columns[0].parse::<i64>();
if timestamp.is_err() {
return None;
}
datatime = UTC.timestamp(timestamp.unwrap(), 0).to_string();
columns[0] = datatime.as_str();
Some(columns.join(","))
}
I believe the problem is that both datatime and columns are owned, and columns is dropped after datatime, so for a short moment columns would contain an invalid reference.
I would change it to not go through columns, but instead populate the new String directly first with the transformed first column, then pushing in the rest.
That way should also make it possible to simply iterate over the split values, and not require the intermediate Vec at all.
fn process_line(line: String) -> Option<String> {
// make an iterator but don't collect
let mut columns = line.split(',');
// take the first item and see if its a timestamp
let timestamp = match columns.next() {
Some(value) => match value.parse::<i64>() {
Ok(ts) => ts,
Err(_) => return None,
},
None => return None,
};
// make a string containing only the first value
let mut output = UTC.timestamp(timestamp, 0).to_string();
// push the rest directly from the splitting iterator
for item in columns {
output.push(',');
output.push_str(item);
}
Some(output)
}
This could probably be shortened, but I kept it a bit more explicit.
Edit: Forgot the separating commas
Edit 2: Push the separator as char, no need for it to be a &str