I have recently started learning Rust and I currently am trying to create a collection of data from a fake API in order to manipulate the data after fetching and deserializing it:
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct Post {
user_id: i32,
id: i32,
title: String,
body: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Begin: important part
let posts = reqwest::get("https://jsonplaceholder.typicode.com/posts")
.await?
.json::<Vec<Post>>()
.await?;
let posts = posts
.iter()
.map(|post| post) // Imagine manipulating the post here, somehow
.collect::<Vec<&Post>>();
// End: important part
println!("{:#?}", posts);
Ok(())
}
This works fine. However, what I don't understand is, why the above works, while it does not work when try to do the whole operation in one go, like this:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Begin: important part
let posts = reqwest::get("https://jsonplaceholder.typicode.com/posts")
.await?
.json::<Vec<Post>>()
.await?
.iter()
.map(|post| post) // Imagine manipulating the post here, somehow
.collect::<Vec<&Post>>();
// End: important part
println!("{:#?}", posts);
Ok(())
}
without the error:
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:15:17
|
15 | let posts = reqwest::get("https://jsonplaceholder.typicode.com/posts")
| _________________^
16 | | .await?
17 | | .json::<Vec<Post>>()
18 | | .await?
| |_______________^ creates a temporary value which is freed while still in use
...
21 | .collect::<Vec<&Post>>();
| - temporary value is freed at the end of this statement
...
24 | println!("{:#?}", posts);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
I was expecting these two ways of approaching the problem to yield the same result and I don't understand why one works while the other yields an error and quite frankly I don't understand the error message either.
What exactly am I doing wrong, here? I suspect that it might have something to to with traits, but I don't see why it wouldn't fail (or succeed, respectively) in both cases then.
The question you should ask yourself is: in the second example, you want to collect to vectors of borrowed Post
s; who owns the Post
s? In the first example, the vector is owned by the variable posts
, and therefore you can iterate over references of elements within the vector; but in the second case, the vector is built by the .json()
method, moved "to you", but you don't store it somewhere, you immediately consume it to retrieve references to what it contains; once the statement ends, the intermediate vector that was built will be dropped (ie. freed) and the references will be invalid.
Maybe you didn't get a nice error message due to manipulation in the .map(...)
call, however the error message I get with your example is
error[E0716]: temporary value dropped while borrowed
--> src/lib.rs:18:17
|
18 | let posts = reqwest::get("https://jsonplaceholder.typicode.com/posts")
| _________________^
19 | | .await?
20 | | .json::<Vec<Post>>()
21 | | .await?
| |_______________^ creates a temporary value which is freed while still in use
...
24 | .collect::<Vec<&Post>>();
| - temporary value is freed at the end of this statement
...
28 | println!("{:#?}", posts);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
You see that Rust suggests you to do exactly what you did.