在使用期货时,是否有更符合人体工程学的语法?

问题描述 投票:0回答:2

这是使用Tokio运行返回未来的函数的示例:

use futures::sync::oneshot;
use futures::Future;
use std::thread;
use std::time::Duration;
use tokio;

#[derive(Debug)]
struct MyError {
    error_code: i32,
}

impl From<oneshot::Canceled> for MyError {
    fn from(_: oneshot::Canceled) -> MyError {
        MyError { error_code: 1 }
    }
}

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (sx, rx) = oneshot::channel();
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(100));
        sx.send(100).unwrap();
    });
    return rx.map_err(|e| MyError::from(e));
}

fn main() {
    tokio::run(deferred_task().then(|r| {
        println!("{:?}", r);
        Ok(())
    }));
}

然而,当有问题的函数(即deferred_task)非常重要时,代码变得复杂得多,因为?操作似乎不容易与返回未来混合:

fn send_promise_to_worker(sx: oneshot::Sender<i32>) -> Result<(), ()> {
    // Send the oneshot somewhere in a way that might fail, eg. over a channel
    thread::spawn(move || {
        thread::sleep(Duration::from_millis(100));
        sx.send(100).unwrap();
    });
    Ok(())
}

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (sx, rx) = oneshot::channel();
    send_promise_to_worker(sx)?; // <-------- Can't do this, because the return is not a result
    return rx.map_err(|e| MyError::from(e));
}

一个Future是一个Result,将它包装在结果中是没有意义的,它打破了impl Future返回类型。

相反,你得到一个深度嵌套的链:

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (sx, rx) = oneshot::channel();
    match query_data() {
        Ok(_i) => match send_promise_to_worker(sx) {
            Ok(_) => Either::A(rx.map_err(|e| MyError::from(e))),
            Err(_e) => Either::B(futures::failed(MyError { error_code: 2 })),
        },
        Err(_) => Either::B(futures::failed(MyError { error_code: 2 })),
    }
}

full code

你拥有的结果越多,嵌套越深;正是?算子正常解决的问题。

我错过了什么吗?是否有一些语法糖可以使这更容易?

error-handling syntax rust future
2个回答
1
投票

我不知道async / await语法如何能够明确地帮助你使用Either。最终,你仍然需要返回一个具体的类型,这就是Either提供的。然而,async / await将减少对Future::mapFuture::and_then等组合器的需求。

也可以看看:


话虽这么说,你不需要在这里使用Either

你有连续的Result返回函数,所以你可以从JavaScript借用一个技巧并使用IIFE来使用?运算符。然后,我们可以将组合的Result“提升”为未来,并将其与接收器的未来联系起来:

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (tx, rx) = oneshot::channel();

    let x = (|| {
        let _i = query_data().map_err(|_| MyError { error_code: 1 })?;
        send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;
        Ok(())
    })();

    future::result(x).and_then(|()| rx.map_err(MyError::from))
}

根据我的理解,未来,LIFE可以用try块替换。

您也可以采用其他方式将所有内容转换为未来:

fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
    let (tx, rx) = oneshot::channel();

    query_data()
        .map_err(|_| MyError { error_code: 1 })
        .into_future()
        .and_then(|_i| {
            send_promise_to_worker(tx)
                .map_err(|_| MyError { error_code: 2 })
                .into_future()
        })
        .and_then(|_| rx.map_err(MyError::from))
}

这将有助于async / await语法:

async fn deferred_task() -> Result<i32, MyError> {
    let (tx, rx) = oneshot::channel();

    query_data().map_err(|_| MyError { error_code: 1 })?;

    send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;

    let v = await! { rx }?;

    Ok(v)
}

我还看到通过在Either特征中添加leftright方法来构造Future的改进语法:

foo.left();
// vs
Either::left(foo);

但是,这不会出现在任何当前的实现中。

FutureResult

不它不是。

有两个相关的Futures可以谈论:

值得注意的是,Future::poll返回的类型可以处于两种状态:

  • 完成
  • 不完整

在期货箱中,“成功”和“失败”与“完整”相关联,而在标准库中则不然。在箱子里,Result实现了IntoFuture,在标准库中你可以使用future::ready。这两个都允许将Result转换为未来,但这并不意味着Result是一个未来,只不过说Vec<u8>是一个迭代器,即使它可以转换成一个。

有可能?运算符(由Try特性提供动力)将被增强为自动从Result转换为特定类型的Future,或者Result甚至可以直接实现Future,但我还没有听说过任何此类计划。


0
投票

是否有一些语法糖可以使这更容易?

是的,它被称为async / await,但它还没有为广泛消费做好准备。它只在每晚支持,它使用一个略有不同的期货版本,Tokio仅通过互操作库支持,导致额外的语法开销,并且整个文档的文档仍然不稳定。

以下是一些相关链接:

What is the purpose of async/await in Rust? https://jsdw.me/posts/rust-asyncawait-preview/ https://areweasyncyet.rs/

© www.soinside.com 2019 - 2024. All rights reserved.