Как решить "не может вернуть значение, ссылающееся на локальные данные" при использовании потоков и асинхронного / await? - PullRequest
1 голос
/ 30 марта 2020

Я изучаю Rust, особенно многопоточность и асин c запросы параллельно.

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

main.rs

use std::thread;

struct Request {
    url: String,
}

impl Request {
    fn new(name: &str) -> Request {
        Request {
            url: name.to_string(),
        }
    }

    async fn call(&self, x: &str) -> Result<(), Box<dyn std::error::Error>> {
        let resp = reqwest::get(x).await;
        Ok(())
    }
}

#[tokio::main]
async fn main() {
    let requests = vec![
        Request::new("https://www.google.com/"),
        Request::new("https://www.google.com/"),
    ];
    let handles: Vec<_> = requests
        .into_iter()
        .map(|request| {
            thread::spawn(move || async {
                request.call(&request.url).await;
            })
        })
        .collect();

    for y in handles {
        println!("{:?}", y);
    }
}
error[E0515]: cannot return value referencing local data `request`
  --> src/main.rs:29:35
   |
29 |               thread::spawn(move || async {
   |  ___________________________________^
30 | |                 request.call(&request.url).await;
   | |                 ------- `request` is borrowed here
31 | |             })
   | |_____________^ returns a value referencing data owned by the current function

Автомобиль go. томл

[dependencies]
reqwest = "0.10.4"
tokio = { version = "0.2", features = ["full"] }

1 Ответ

3 голосов
/ 30 марта 2020

Как и замыкания, блоки async фиксируют свои переменные настолько слабо, насколько это возможно. В порядке предпочтения:

  1. неизменяемая ссылка
  2. изменяемая ссылка
  3. по значению

Это определяется тем, как используется переменная в блоке закрытия / асин c. В вашем примере request используется только по ссылке, поэтому он захватывается только по ссылке:

async {
    request.call(&request.url).await;
}

Однако необходимо перенести владение переменной в блок asyn c, чтобы переменная все еще жива, когда будущее в конечном счете выполнено. Как и замыкания, это делается с помощью ключевого слова move:

thread::spawn(move || async move {
    request.call(&request.url).await;
})

См. Также:


Это очень маловероятно что вы хотите смешивать нити и асин c на данный момент в вашем понимании. Один из них по своей сути блокирует, а другой ожидает, что код не будет блокироваться. Вы должны следовать примеру, описанному в Как я могу выполнять параллельные асинхронные HTTP-запросы GET с reqwest? вместо этого.

См. Также:

...