我试图使用hyper获取HTML页面的内容,并希望同步返回future 的输出.我意识到我本可以 Select 一个更好的例子,因为同步HTTP请求已经存在,但我更感兴趣的是理解我们是否可以从异步计算中返回值.

extern crate futures;
extern crate hyper;
extern crate hyper_tls;
extern crate tokio;

use futures::{future, Future, Stream};
use hyper::Client;
use hyper::Uri;
use hyper_tls::HttpsConnector;

use std::str;

fn scrap() -> Result<String, String> {
    let scraped_content = future::lazy(|| {
        let https = HttpsConnector::new(4).unwrap();
        let client = Client::builder().build::<_, hyper::Body>(https);

        client
            .get("https://hyper.rs".parse::<Uri>().unwrap())
            .and_then(|res| {
                res.into_body().concat2().and_then(|body| {
                    let s_body: String = str::from_utf8(&body).unwrap().to_string();
                    futures::future::ok(s_body)
                })
            }).map_err(|err| format!("Error scraping web page: {:?}", &err))
    });

    scraped_content.wait()
}

fn read() {
    let scraped_content = future::lazy(|| {
        let https = HttpsConnector::new(4).unwrap();
        let client = Client::builder().build::<_, hyper::Body>(https);

        client
            .get("https://hyper.rs".parse::<Uri>().unwrap())
            .and_then(|res| {
                res.into_body().concat2().and_then(|body| {
                    let s_body: String = str::from_utf8(&body).unwrap().to_string();
                    println!("Reading body: {}", s_body);
                    Ok(())
                })
            }).map_err(|err| {
                println!("Error reading webpage: {:?}", &err);
            })
    });

    tokio::run(scraped_content);
}

fn main() {
    read();
    let content = scrap();

    println!("Content = {:?}", &content);
}

示例编译后,对read()的调用成功,但对scrap()的调用出现以下错误消息:

Content = Err("Error scraping web page: Error { kind: Execute, cause: None }")

我明白,在future 致电.wait()之前,我没有正确启动任务,但我找不到正确的方法,假设这是可能的.

推荐答案

Standard library futures

让我们用这个作为我们的minimal, reproducible example:

async fn example() -> i32 {
    42
}

拨打executor::block_on:

use futures::executor; // 0.3.1

fn main() {
    let v = executor::block_on(example());
    println!("{}", v);
}

东京

在任何函数上使用tokio::main属性(不仅仅是main!)要将其从异步函数转换为同步函数:

use tokio; // 0.3.5

#[tokio::main]
async fn main() {
    let v = example().await;
    println!("{}", v);
}

tokio::main是一个宏,可以改变这一点

#[tokio::main]
async fn main() {}

对此:

fn main() {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async { {} })
}

这在引擎盖下使用了Runtime::block_on,所以你也可以这样写:

use tokio::runtime::Runtime; // 0.3.5

fn main() {
    let v = Runtime::new().unwrap().block_on(example());
    println!("{}", v);
}

对于测试,可以使用tokio::test.

异步标准

使用main函数的async_std::main属性将其从异步函数转换为同步函数:

use async_std; // 1.6.5, features = ["attributes"]

#[async_std::main]
async fn main() {
    let v = example().await;
    println!("{}", v);
}

对于测试,可以使用async_std::test.

Futures 0.1

让我们用这个作为我们的minimal, reproducible example:

use futures::{future, Future}; // 0.1.27

fn example() -> impl Future<Item = i32, Error = ()> {
    future::ok(42)
}

对于简单的情况,您只需拨打wait:

fn main() {
    let s = example().wait();
    println!("{:?}", s);
}

然而,这带来了一个相当严重的警告:

此方法不适用于调用事件循环或类似的I/O情况,因为它会阻止事件循环进行(这会阻止线程).只有在保证与此future 相关的阻塞工作将由另一个线程完成时,才应该调用此方法.

东京

如果你使用的是东京 0.1,你应该使用东京的Runtime::block_on:

use tokio; // 0.1.21

fn main() {
    let mut runtime = tokio::runtime::Runtime::new().expect("Unable to create a runtime");
    let s = runtime.block_on(example());
    println!("{:?}", s);
}

如果你看一下block_on的实现,它实际上会将future 的结果发送到一个通道,然后在该通道上调用wait!这很好,因为东京保证运行future 直至完成.

另见:

Rust相关问答推荐

为什么BitVec缺少Serialize trait?

获取字符串切片(&;str)上的切片([ia..ib])返回字符串

除了调用`waker.wake()`之外,我如何才能确保future 将再次被轮询?

如何为 struct 字段设置新值并在Ruust中的可变方法中返回旧值

Rust中WPARAM和VIRTUAL_KEY的比较

对于rustc编译的RISC-V32IM二进制文件,llvm objdump没有输出

是否可以使用Serde/Rust全局处理无效的JSON值?

我应该如何表达具有生命周期参数的类型的总排序,同时允许与不同生命周期进行比较?

Rust LinkedList 中的borrow 判断器错误的原因是什么?

当锁被释放时,将锁包装到作用域中是否会发生变化?

方法可以被误认为是标准特性方法

Rust中是否可以在不复制的情况下从另一个不可变向量创建不可变向量?

缺失serde的字段无法设置为默认值

有没有办法通过命令获取 Rust crate 的可安装版本列表?

我什么时候应该使用特征作为 Rust 的类型?

将 Futures 的生命周期特征绑定到 fn 参数

判断对象是 PyDatetime 还是 Pydate 的实例?

如何存储返回 Future 的闭包列表并在 Rust 中的线程之间共享它?

有没有办法隐藏类型定义?

为什么我可以在没有生命周期问题的情况下内联调用 iter 和 collect?