使用期货时 Either 是否有更符合人体工程学的语法?
Is there a more ergonomic syntax for Either when using futures?
这是一个使用 Tokio 来 运行 一个 return 未来的函数的例子:
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
)非常重要时,我编写代码时会变得复杂得多,因为 ?
操作似乎并不容易与 return 混合在一起的未来:
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));
}
AFuture
是一个Result
,在result中包裹起来没有意义,破坏了impl Future
return类型。
相反,您得到一个深度嵌套的链:
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 })),
}
}
结果越多,嵌套越深;正是 ?
运算符正常解决的问题。
我错过了什么吗?是否有一些语法糖可以使这更容易?
Is there some syntax sugar to make this easier?
是的,它叫做 async/await,但它还没有准备好广泛使用。它仅在夜间受支持,它使用的期货版本略有不同,Tokio 仅通过互操作库支持该版本,这会导致额外的语法开销,并且整个过程的文档仍然参差不齐。
以下是一些相关链接:
https://jsdw.me/posts/rust-asyncawait-preview/
https://areweasyncyet.rs/
我看不出 async
/ await
语法如何明确地帮助您使用 Either
。最终,您仍然需要 return 一个具体类型,而这正是 Either
提供的。然而,async
/ await
将减少对 Future::map
或 Future::and_then
等组合器的需求。
另请参阅:
也就是说,您不需要在此处使用 Either
。
你有连续的Result
-returning函数,所以你可以借用JavaScript的技巧并使用IIFE来使用?
操作员。然后,我们可以 "lift up" 组合 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))
}
在未来,IIFE 可以用 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)
}
我还看到通过向 Future
特征添加 left
和 right
方法来改进构造 Either
的语法:
foo.left();
// vs
Either::left(foo);
但是,这不会出现在当前的任何实现中。
A Future
is a Result
不,不是。
有两个相关的Future
要讲:
值得注意的是,Future::poll
return 是一种可以处于两种状态的类型:
- 完成
- 不完整
在 futures crate 中,"success" 和 "failure" 绑定到 "complete",而在标准库中它们不是。在板条箱中,Result
实现了 IntoFuture
, and in the standard library you can use future::ready
。这两个都允许将 Result
转换为未来,但这并不意味着 Result
是 未来,只不过是说 Vec<u8>
是一个迭代器,尽管它可以转换为一个。
?
运算符(由 Try
特性提供支持)可能会得到增强,以自动从 Result
转换为特定类型的 Future
,或者 Result
甚至会直接实施 Future
,但我没有听说过任何此类计划。
这是一个使用 Tokio 来 运行 一个 return 未来的函数的例子:
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
)非常重要时,我编写代码时会变得复杂得多,因为 ?
操作似乎并不容易与 return 混合在一起的未来:
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));
}
AFuture
是一个Result
,在result中包裹起来没有意义,破坏了impl Future
return类型。
相反,您得到一个深度嵌套的链:
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 })),
}
}
结果越多,嵌套越深;正是 ?
运算符正常解决的问题。
我错过了什么吗?是否有一些语法糖可以使这更容易?
Is there some syntax sugar to make this easier?
是的,它叫做 async/await,但它还没有准备好广泛使用。它仅在夜间受支持,它使用的期货版本略有不同,Tokio 仅通过互操作库支持该版本,这会导致额外的语法开销,并且整个过程的文档仍然参差不齐。
以下是一些相关链接:
https://jsdw.me/posts/rust-asyncawait-preview/
https://areweasyncyet.rs/
我看不出 async
/ await
语法如何明确地帮助您使用 Either
。最终,您仍然需要 return 一个具体类型,而这正是 Either
提供的。然而,async
/ await
将减少对 Future::map
或 Future::and_then
等组合器的需求。
另请参阅:
也就是说,您不需要在此处使用 Either
。
你有连续的Result
-returning函数,所以你可以借用JavaScript的技巧并使用IIFE来使用?
操作员。然后,我们可以 "lift up" 组合 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))
}
在未来,IIFE 可以用 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)
}
我还看到通过向 Future
特征添加 left
和 right
方法来改进构造 Either
的语法:
foo.left();
// vs
Either::left(foo);
但是,这不会出现在当前的任何实现中。
A
Future
is aResult
不,不是。
有两个相关的Future
要讲:
值得注意的是,Future::poll
return 是一种可以处于两种状态的类型:
- 完成
- 不完整
在 futures crate 中,"success" 和 "failure" 绑定到 "complete",而在标准库中它们不是。在板条箱中,Result
实现了 IntoFuture
, and in the standard library you can use future::ready
。这两个都允许将 Result
转换为未来,但这并不意味着 Result
是 未来,只不过是说 Vec<u8>
是一个迭代器,尽管它可以转换为一个。
?
运算符(由 Try
特性提供支持)可能会得到增强,以自动从 Result
转换为特定类型的 Future
,或者 Result
甚至会直接实施 Future
,但我没有听说过任何此类计划。