I am trying to piece out this piece of borrowing puzzle :
I have a function with this signature :
async fn download_stars<T: AsRef<str>>(
link: T,
mut stop_condition: impl FnMut(Option<&str>) -> bool,
) -> Result<Vec<Query>, reqwest::Error> {
// Some code elided
let mut stars: Vec<Query> = Vec::new();
let mut next_link: Option<&str> = Some(link.as_ref());
while stop_condition(next_link) {
if let Some(link) = next_link {
let res = client.get(&*link).send().await?;
let headers = &res.headers();
next_link = extract_link_next(headers);
let mut s = res.json().await?;
stars.append(&mut s);
}
}
Ok(stars)
}
And of course, the Rust compiler sends back :
error[E0597]: `res` does not live long enough
--> src/main.rs:58:28
|
55 | while stop_condition(next_link) {
| --------- borrow later used here
...
58 | let headers = &res.headers();
| ^^^ borrowed value does not live long enough
...
62 | }
| - `res` dropped here while still borrowed
error[E0505]: cannot move out of `res` because it is borrowed
--> src/main.rs:60:25
|
55 | while stop_condition(next_link) {
| --------- borrow later used here
...
58 | let headers = &res.headers();
| --- borrow of `res` occurs here
59 | next_link = extract_link_next(headers);
60 | let mut s = res.json().await?;
| ^^^ move out of `res` occurs here
error: aborting due to 2 previous errors; 1 warning emitted
Which seems totally fair but how would I solve it ?
let headers = &res.headers();
next_link = extract_link_next(headers);
That makes next_link borrow from headers, and headers are valid only where their let is. This code means:
while … {
let headers = &res.headers();
next_link = extract_link_next(headers);
…
drop(headers);
}
and when headers are dropped, the value in next_link is gone too. It's essential to understand that &str doesn't store the string. It only says "someone else has a string over there".
The easiest solution is to copy the value out of headers, so use:
let mut next_link: Option<String> = Some(link.as_ref().to_string())
You can try moving the value out of headers. You can use Cow if you really want to avoid the first redundant alloc.
error[E0382]: use of moved value
--> src/main.rs:56:21
|
54 | let mut next_link: Option<String> = Some(link.as_ref().to_string());
| ------------- move occurs because `next_link` has type `std::option::Option<std::string::String>`, which does not implement the `Copy` trait
55 | while stop_condition(next_link) {
| --------- value moved here
56 | if let Some(link) = next_link {
| ^^^^ value used here after move
error: aborting due to previous error; 1 warning emitted