如何在稳定的Rust中同步返回在异步Future中计算的值?

Bor*_*ris 6 future rust hyper

我正在尝试使用hyper来获取HTML页面的内容,并希望同步返回将来的输出。我意识到我可以选择一个更好的示例,因为同步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);
}
Run Code Online (Sandbox Code Playgroud)

该示例将编译并且调用read()成功,但是scrap()带有以下错误消息的panic 调用:

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);
}
Run Code Online (Sandbox Code Playgroud)

我了解到,我在呼吁.wait()未来之前未能正确启动任务,但即使有可能,我也找不到正确执行任务的方法。

She*_*ter 13

标准图书馆期货

让我们将其用作最小的,可重现的示例

use futures::future; // 0.3.0-alpha.17
use std::future::Future; 

fn example() -> impl Future<Output = i32> {
    future::ready(42)
}
Run Code Online (Sandbox Code Playgroud)

致电executor::block_on

fn main() {
    let v = futures::executor::block_on(example());
    println!("{}", v);
}
Run Code Online (Sandbox Code Playgroud)

期货0.1

让我们将其用作最小的,可重现的示例

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

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

对于简单的情况,您只需要调用wait

fn main() {
    let s = example().wait();
    println!("{:?}", s);
}
Run Code Online (Sandbox Code Playgroud)

但是,这带有一个非常严重的警告:

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

如果您使用的是Tokio,则应使用Tokio的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);
}
Run Code Online (Sandbox Code Playgroud)

如果您查看的实现block_on,它实际上会将未来的结果发送到某个通道,然后wait在该通道上调用!这很好,因为Tokio保证将来可以完成。

也可以看看:

  • @Amani 如果您认为在 Cargo.toml 中添加一行是一个“障碍”,那么 Rust 可能不是您喜欢使用的语言。更多的东西总是有可能被转移到标准库中,但是并没有真正强烈的推动这样做,特别是当有多种方法来解决一个问题而没有明显的好处时**和**轻松添加依赖项。 (4认同)