首页 > 解决方案 > 如何根据作为参数传递的闭包的返回类型来改变函数的行为?

问题描述

我有一个返回柯里化函数结果的函数:

async fn runit<F, Fut, Ret>(cb: F) -> Result<Ret, MyError>
where
    F: FnOnce() -> Fut,
    Fut: Future<Output = Ret>,
{
    Ok(cb().await)
}

有时F可能会返回 a Result<Ret, MyError>,要求调用者将返回的值解开两次。有没有一种方法可以让函数自动检测是否F已经返回正确的类型并避免调用Ok

标签: rustclosures

解决方案


您可能可以使用具有单独的毯子实现的特征Result和其他类型。就像是:

pub trait IntoResult<T, E> {
    fn into_result(self) -> Result<T, E>;
}

impl<T, E> IntoResult<T, E> for Result<T, E> {
    fn into_result(self) -> Result<T, E> {
        self
    }
}

impl<T, E> IntoResult<T, E> for T {
    fn into_result(self) -> Result<T, E> {
        Ok(self)
    }
}

然后你可以实现runit调用into_result()

async fn runit<F, Fut, Ret, RawRet>(cb: F) -> Result<Ret, MyError>
where
    F: FnOnce() -> Fut,
    Fut: Future<Output = RawRet>,
    RawRet: IntoResult<Ret, MyError>,
{
    cb().await.into_result()
}

现在 Rust 将能够推断出Ret满足IntoResulttrait,有效地消除了 inner Result

// one unwrap needed for Result<usize, MyError>
let _x: usize = runit(|| async { 1 }).await.unwrap();
// two unwraps needed because closure returns Result whose error is not MyError
let _y: usize = runit(|| async { Ok::<_, std::io::Error>(1usize) })
    .await
    .unwrap()
    .unwrap();
// one unwrap enough because closure returns Result<usize, MyError>
let _z: usize = runit(|| async { Ok::<_, MyError>(1usize) }).await.unwrap();

操场

在生产中使用它之前,我建议对这种“聪明”非常小心。虽然它在工作时看起来非常好,但它会使函数的签名复杂化,有时它可能需要类型提示,否则就不需要了。anyhow::Error使用诸如将不兼容错误的结果合并为一个结果之类的方法通常更简单。


推荐阅读