如何用人造丝遍历数组?

How to iterate over an array with rayon?

我是 Rust 的新手,我正在尝试使用人造丝从一组 URL 中获取响应

use rayon::prelude::*;
use reqwest::*;

fn main() -> Result<()> {
    let urls = vec!["https://example.com"];
    urls.par_iter().for_each(|url: &&str| ->  Result<()> {
        println!("Hello... {:?}", url);
        let resp = reqwest::blocking::get(url)?.text()?;
        println!("{:#?}", resp);
        Ok(())
    });
    Ok(())
}

但是我收到了这个错误

error[E0271]: type mismatch resolving `<[closure@src\main.rs:252:30: 257:6] as FnOnce<(&&str,)>>::Output == ()`
   --> src\main.rs:252:21
    |
252 |     urls.par_iter().for_each(|url| ->  Result<()> {
    |                     ^^^^^^^^ expected enum `std::result::Result`, found `()`
    |
    = note:   expected enum `std::result::Result<(), reqwest::Error>`
            found unit type `()`

error[E0277]: the trait bound `&&str: IntoUrl` is not satisfied
   --> src\main.rs:254:20
    |
254 |         let resp = reqwest::blocking::get(url)?.text()?;
    |                    ^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoUrl` is not implemented for `&&str`
    | 
   ::: mod.rs:106:15
    |
106 | pub fn get<T: crate::IntoUrl>(url: T) -> crate::Result<Response> {
    |               -------------- required by this bound in `reqwest::blocking::get`
    |
    = help: the following implementations were found:
              <&'a str as IntoUrl>

for_each 取 closure/lambda 其 return 类型是 unit 而不是 Result
? 运算符不能在函数内部使用 /closure/lambda which return unit -> ()
* 用于取消引用和访问底层字符串

src/main.rs

use rayon::prelude::*;

fn main() {
    let urls = vec!["https://example.com"];
    urls.par_iter().for_each(|url: &&str| {
        println!("Hello... {:?}", *url);
        let resp = reqwest::blocking::get(*url).unwrap().text();
        println!("{:#?}", resp);
    });
}

Cargo.toml

[package]
name = "s71275260"
version = "0.1.0"
edition = "2021"

# see more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rayon = "1.5.1"
reqwest = { version = "0.11", features = ["blocking"] }

第二个错误更容易:IntoUrl 没有为 &&str 实现,而是为 &str 实现。所以你需要做的就是取消引用指针:

urls.par_iter().for_each(|url: &&str| -> Result<()> {
    let url = *url;

或者

urls.par_iter().for_each(|&url: &&str| -> Result<()> {

第一个比较棘手。问题是 for_each() 需要一个 returns () 的闭包,并给它一个 returns Result 不起作用的闭包。

因为您希望在请求失败时继续处理其他请求,所以您不能使用 rayon 的 try_for_each(),因为它会在失败时停止所有待处理的请求。相反,您只想忽略失败,也许将其记录下来。使用 unwrap() 是个坏主意:它不仅会停止其他请求,还会中止您的程序! 从不 使用 unwrap(),除非您 100% 确定代码永远不会失败(如果它不明显,最好注释为什么)。您可以放弃 ? 运算符,而只是显式:

urls.par_iter().for_each(|&url| {
    println!("Hello... {:?}", url);
    if let Ok(resp) = reqwest::blocking::get(url) {
        if let Ok(resp) = resp.text() {
            println!("{:#?}", resp);
        }
    }
});

虽然您可以稍微改进一下这段代码,但它仍然很难看。 try blocks 会改善这种情况;在它们稳定下来之前,对于具有多个潜在故障点的块有一个常见的习语:

urls.par_iter().for_each(|&url| {
    _ = (|| -> Result<()> {
        println!("Hello... {:?}", url);
        let resp = reqwest::blocking::get(url)?.text()?;
        println!("{:#?}", resp);
        Ok(())
    })();
});

这利用闭包的能力来使用 ? 运算符来简化代码。

但是请注意 rayon 不适用于 IO-bound 任务:使用像 tokio.

这样的异步框架