Сбор Vec с пользовательской структурой

Недавно я начал изучать Rust и в настоящее время пытаюсь создать набор данных из поддельного API, чтобы манипулировать данными после их извлечения и десериализации:

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(())
}

Это прекрасно работает. Однако я не понимаю, почему приведенное выше работает, а не работает при попытке выполнить всю операцию за один раз, например:

#[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(())
}

Детская площадка

без ошибки:

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

Я ожидал, что эти два подхода к проблеме дадут один и тот же результат, и я не понимаю, почему один работает, а другой выдает ошибку, и, честно говоря, я тоже не понимаю сообщения об ошибке.

Что именно я делаю неправильно, здесь? Я подозреваю, что у него может быть что-то связанное с чертами, но я не понимаю, почему тогда он не потерпит неудачу (или не преуспеет, соответственно) в обоих случаях.

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
0
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Error: value of type `Vec<&Post>` cannot be built from `std::iter::Iterator<Item=&Vec<Post>>

указывает на различия в возвращаемых типах обоих подходов.

Во втором подходе вы пытаетесь собрать Vec<&Post> непосредственно из Iterator<Item=&Post>, возвращенного .iter() в десериализованном Vec<Post>. Это не работает, потому что метод .iter() возвращает итератор по ссылкам на объекты Post, а не ссылки на ссылки на объекты Post (именно это Vec<&Post> expects).

Ответ принят как подходящий

Вопрос, который вы должны задать себе: во втором примере вы хотите собрать векторы заимствованных Posts; кому принадлежат Posts? В первом примере вектор принадлежит переменной posts, поэтому вы можете перебирать ссылки на элементы внутри вектора; но во втором случае вектор строится методом .json(), перемещается «к вам», но вы его не храните где-то, а сразу потребляете для извлечения ссылок на то, что в нем содержится; как только оператор завершится, построенный промежуточный вектор будет удален (т. е. освобожден), а ссылки станут недействительными.

Возможно, вы не получили приятное сообщение об ошибке из-за манипуляций в вызове .map(...), однако сообщение об ошибке, которое я получаю с вашим примером,

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

Вы видите, что Rust предлагает вам сделать именно то, что вы сделали.

Однако в этом конкретном случае вы не должны следовать подсказке компилятора, а скорее замените iter() на into_iter() и соберете в Vec<Post>. В противном случае будет невозможно сделать какие-либо модификации на постах. Все, что вы могли бы сделать, это собрать ссылки на точные сообщения в первом векторе во второй вектор, что кажется довольно бессмысленным.

Sven Marnach 02.05.2023 15:12

@SvenMarnach Спасибо, это помогло мне почти так же, как и правильный ответ, потому что он заставил меня щелкнуть что-то, что уже должно было щелкнуть.

Odiumediae 02.05.2023 20:02

Я полностью неправильно понял полезную ошибку компилятора, но ваш ответ открыл мне глаза, спасибо!

Odiumediae 02.05.2023 20:08

Другие вопросы по теме