【问题标题】:How can I alter the behavior of a function based on the return type of a closure passed as an argument?如何根据作为参数传递的闭包的返回类型来改变函数的行为?
【发布时间】:2021-03-13 14:47:12
【问题描述】:

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

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

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

【问题讨论】:

标签: rust closures


【解决方案1】:

您也许可以为 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 将能够推断出满足 IntoResult 特征的 Ret,有效地消除了内部的 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();

Playground

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

【讨论】:

  • 我很惊讶它的工作原理,因为我认为第二个 impl 中的 T 可能是 Result&lt;Tprime, E&gt;,从而导致需要专门化的重叠实现问题。
  • @Shepmaster 我的想法很准确(在我在某处看到这种模式之前)。我认为它可以工作,因为Result 是在板条箱之外定义的,或者是由孤儿规则特例的,但情况似乎并非如此,因为如果你switch to a locally defined type 它将编译。剩下的解释是它以某种方式击中泛型,使其正常 - 甚至是编译器错误?
  • 应该和the changes in 1.41有关,但我还是需要深入挖掘才能理解。
  • @Shepmaster 对于它的价值,它是compiles with 1.40(也是with local Result),所以我想这排除了1.41的变化。
  • 询问in Zulip,有人指出,通过将泛型添加到特征定义中,我们实际上创建了多个特征,因此不可能有重叠。如果泛型被删除或转换为关联类型,那么同一类型将有多个相同特征的实现。
猜你喜欢
  • 1970-01-01
  • 2021-03-25
  • 2021-04-02
  • 1970-01-01
  • 2019-11-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-23
  • 1970-01-01
相关资源
最近更新 更多