Value not long enough when using Chunks iterator for multiple threads
This is a simplified example of my situation:
use std::sync::mpsc::{Sender, Receiver};
use std::sync::mpsc;
use std::thread;
struct User {
reference: String,
email: String,
}
fn main() {
let rows = vec![
vec!["abcd", "test@test.com"],
vec!["efgh", "test1@test.com"],
vec!["wfee", "test2@test.com"],
vec!["rrgr", "test3@test.com"],
];
let chunk_len = (rows.len() / 2) as usize;
let mut chunks = Vec::new();
for chunk in rows.chunks(chunk_len) {
chunks.push(chunk);
}
let (tx, rx): (Sender<Vec<User>>, Receiver<Vec<User>>) = mpsc::channel();
for chunk in chunks {
let thread_tx = tx.clone();
thread::spawn(move || {
let result = chunk
.iter()
.map(|row| {
User {
reference: row[0].to_string(),
email: row[1].to_string(),
}
})
.collect::<Vec<User>>();
thread_tx.send(result).unwrap();
});
}
let mut users = Vec::new();
for _ in 0..chunk_len {
users.push(rx.recv());
}
}
and it throws an error
error[E0597]: `rows` does not live long enough --> src/main.rs:20:18 | 20 | for chunk in rows.chunks(chunk_len) { | ^^^^ does not live long enough ... 47 | } | - borrowed value only lives until here | = note: borrowed value must be valid for the static lifetime...
I tried to upgrade to chunks.push(chunk.clone());
but the error still won't go away. What am I missing here?
source to share
The error looks wrong, but it is correct. Your problem is what chunks()
gives the slice iterator to the original vector:
impl<'a, T> Iterator for Chunks<'a, T> {
type Item = &'a [T];
}
You are trying to use this slice on an spawn()
ed stream , which requires the closure to have a lifetime 'static
:
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static,
T: Send + 'static,
Your iterator has a lifetime 'static
because it contains references to a vector allocated at runtime.
You said you tried clone()
, but this only clones the slice, it doesn't give you a new vector. For this you need to use to_owned()
:
for chunk in rows.chunks(chunk_len) {
chunks.push(chunk.to_owned());
}
And so, it compiles .
source to share
The problem you are facing is what std::slice::Chunks
is an iterator that returns references to the original vector. Therefore in
let mut chunks = Vec::new();
for chunk in rows.chunks(chunk_len) {
chunks.push(chunk);
}
chunks
ends up being a vector of references in rows
(or more precisely, a collections::vec::Vec<&[collections::vec::Vec<&str>]>
).
You will need to create a new one Vec
inside this loop to copy the data. Unfortunately, the call is clone
not enough (it creates a clone of the link, which is still a link). You can do:
for chunk in rows.chunks(chunk_len) {
let mut v = Vec::new();
v.extend(chunk.iter().cloned());
chunks.push(v);
}
EDIT : Vladimir has a much better way to clone slice => chunks.push(chunk.to_owned())
.
source to share