或结果>,rust"/>

首页 > 解决方案 > Rust:如何定义一个可以返回任一结果的闭包或结果>

问题描述

我正在尝试定义一个通用闭包,以便它可以返回Result包含任何类型错误的 a ,特别是在 a 中的特定错误或通用错误Box。我有一个游乐场示例,说明了我在这里尝试做的事情,但这是一个缩短的版本:

fn try_with_context<T, F: FnOnce() -> Result<T, Box<dyn Error>>>(
    context: &str,
    block: F,
) -> Result<T, Box<dyn Error>> {
    match block() {
        Ok(v) => Ok(v),
        Err(e) => Err(e),
    }
}

我想支持给定block的能够返回 aResult<T, MyError>Result<T, Box<dyn Error>>,但当前返回Err(MyError)失败:


fn error_func() -> Result<u8, MyError> {
    Err(MyError {})
}

fn do_something() {    
    // This won't work: Expected Box, found struct MyError
    try_with_context("something", || error_func());
}

现在,有几个解决方法可以使这项工作:

try_with_context("something", || Ok(error_func()));

或者

try_with_context("something", || error_func().map_err(Into::into));

两者都修复它,但我希望有一种方法来定义它,这样这些变通方法都不是必需的(该try_with_context方法是库的一部分,我发现用户必须使用这些方法是不直观的,所以我我希望避免需要它)。

我已经尝试引入另一种泛型来尝试解决它:

fn try_with_context_2<T, U, F: FnOnce() -> Result<T, U>>(
    context: &str,
    block: F,
) -> Result<T, Box<dyn Error>>
where
    U: Error + 'static,
{
    match block() {
        Ok(v) => Ok(v),
        Err(e) => Err(e.into()),
    }
}

但不幸的是,这只是扭转了这里的问题:现在Err(MyError{})版本很好,但抱怨返回的版本Box<dyn Error>

第三个版本确实解决了这两个简单的用例:

fn try_with_context_3<T, U, F: FnOnce() -> Result<T, U>>(
    context: &str,
    block: F,
) -> Result<T, Box<dyn Error>>
where
    U: Into<Box<dyn Error>>,
{
    match block() {
        Ok(v) => Ok(v),
        Err(e) => Err(e.into()),
    }
}

但是当我尝试更复杂的版本时block,错误会以奇怪的方式出现,例如:

try_with_context("something", || {
    other_error_func()?;
    error_func()?
});

抱怨'?' couldn't convert the error to 'MyError',但是:

try_with_context("something", || {    
    error_func()?;
    other_error_func()
});

很好。

有没有一种好的方法来定义可以在没有变通方法的情况下处理所有这些情况的闭包?

编辑:在阅读@isaactfa 的答案后,我对此进行了更多思考,并意识到它确实归结为一些基本的东西:我正在有效地尝试定义一个可以返回实现的特定类型Errora 的方法Box<dyn Error>,所以我需要泛型足够广泛以允许这两种可能性中的任何一种,但这意味着(至少在我尝试过的每次迭代中)如果该函数的显式返回返回一个特定的实现类型Error,那么编译器将尝试并强制任何更早的?样式返回到该特定错误类型,这不起作用。我仍然很想听听是否有一种聪明的方法来解决这个问题,但与此同时,我将使用我上面提到的一种解决方法(并且可能会定义一些辅助函数包装器.map_err(Into::into)以使其更符合人体工程学。

标签: rust

解决方案


问题是在

try_with_context_3("something", || {
    other_error_func()?;
    error_func()
});

你正在返回 aResult<_, MyError>所以 Rust 期望它是返回类型。?操作员足够聪明,可以尝试将它遇到的所有其他错误类型转换为实际返回的错误类型。这实际上只给您留下两个选择:强制闭包始终返回 a Box<dyn Error + 'static>,并使其符合人体工程学,可能通过为您的自定义错误类型提供适当的构造函数,或者提供一种?将 a 转换Box<dyn Error + 'static>为 a 的方法MyError

impl From<Box<dyn Error + 'static>> for MyError {
    fn from(_t: Box<dyn Error + 'static>) -> Self {
        MyError
    }
}

有了它,这将编译:

fn do_something() {
    try_with_context_3("something", || error_func());

    try_with_context_3("something", || other_error_func());

    try_with_context_3("something", || {
        other_error_func()?;
        error_func()
    });

    try_with_context_3("something", || {
        error_func()?;
        other_error_func()
    });
}

推荐阅读