【问题标题】:rust extend built-in enum Result?rust 扩展内置枚举结果?
【发布时间】:2021-09-17 06:45:02
【问题描述】:

tl;dr 是否可以扩展 std::result::Result 以添加我自己的变体来表示“事情还好,但也......”并保留 impl Result 方法,如 @987654328 @?


我想扩展 Result 以指示函数调用者可用于特殊情况的其他状态。

use std::result::Result
use std::io::Error;

/// Extend Result to also signal "things are okay but check on things"
enum ResultExt<T, E> {
    Result<T, E>,
    OkButCheckThings(T),
}

pub fn do_stuff() -> ResultExt<u64, Error> {
  // ...
}

pub fn main() -> {
  let var = match do_stuff() {
    Ok(val) => { val },
    Err(err) => { 0 },
    OkButCheckThings(val) => { check_things(); val },
  }
  dbg!(var);
}

它是possible to plainly extend an Enum但我也想使用底层的Result&lt;T, E&gt; 函数,例如is_ok

let var2 = do_stuff();
if var2.is_ok() {
   println!("It is totally Ok, nothing to check!");
}

我创建了一个rust playground example,它成功地扩展了Result&lt;T, E&gt;,但是扩展的enum 不能使用像is_ok() 这样的函数。



实际用例是一个调用 std::io::Read 的函数,可能需要“修改”返回的 Result 以指示除 OkErr 之外的其他状态。但我希望这些不同的“元状态”被一个enum 捕获,而不是返回各种其他bool 标志(我想避免使用(Result&lt;T&gt;, bool, bool) 返回签名。这将允许一个干净的match 声明所有可能的状态;OkErr"Okay but...""Err but ..." 等。

【问题讨论】:

  • 看来你要的是Result&lt;YourOwnResultEnum, Error&gt;
  • 您好,这个可以帮上忙吗? stackoverflow.com/questions/25214064/…
  • 我在问题中提到了stackoverflow.com/questions/25214064/…。它与这个问题不同。我想扩展Enum 并且仍然使用impl ... 函数
  • 您不会想要这样,因为该方法不再正确。例如。 is_ok 是通过检查 Ok 大小写来实现的,而 is_err 是通过返回 !self.is_ok() 来实现的。因此,如果您可以扩展 Result 并“继承其方法”,那么您最终会得到不正确的方法,因为 is_err 将为您的新的非错误案例返回 true。当然,is_err 可以通过其他方式实现以使其工作,但关键是扩展enums 不可能对所有方法都有意义。另一个简单的例子:继承的transpose 应该在扩展的Result 上做什么?

标签: rust enums


【解决方案1】:

目前没有“扩展”和 enum 本身的方式。 但这可以通过将您自己的枚举类型嵌入结果本身来简单地解决。

简单的例子,和你的类似:

use std::fmt::Display;

enum StuffToCheck<T> {
    Ok(T),
    CheckThis(T),
}

impl<T> StuffToCheck<T>
where
    T: Display + Copy,
{
    pub fn check_things(&self) -> T {
        match self {
            Self::Ok(val) => {
                *val
            }
            Self::CheckThis(val) => {
                println!("Checking stuff for {}", val);
                *val
            }
        }
    }
}

fn do_stuff() -> ResultExt<u64> {
    Ok(StuffToCheck::CheckThis(10))
}

type ResultExt<T> = Result<StuffToCheck<T>, std::io::Error>;

fn main() {
    let var = match do_stuff() {
        Ok(result) => result.check_things(),
        Err(_err) => 0,
    };
    dbg!(var);
}

Playground

您甚至可以使用嵌套模式匹配:

...
match do_stuff() {
    Err(e) => {//handle error}
    Ok(StuffToCheck::Ok(value)) => { value },
    Ok(StuffToCheck::CheckThis(value)) => {
        check_things(value);
        value
    }
}
...

【讨论】:

    【解决方案2】:

    我认为这是 X-Y 问题的一个实例。您可以使用内置结果,您只需要一个不同的错误类型,即返回一个选项:Some(partial_result)None

    例如,您有函数parse,它可以尝试针对格式错误的输入进行调整,但会报告错误。

    pub fn parse(b: &str) -> Result<&str, CustomParseError> {
        // Do something that might fail, 
        if failed(){
           return CustomParseError::new(None)
        } else if partially_failed() {
           return CustomParseError::new(Some(partial_result))
        } else {
            return completeResult
        }
    
    }
    

    这样你有一个 clean 代码路径没有失败,你的所有假设都是正确的,如果它不是 => 而不是展开,你 match 并检查你有哪种情况.这是非常优越的,因为错误通常包含足够的信息,可以让您重新构建出了什么问题,以及可以采取哪些措施来修复它。

    【讨论】:

      猜你喜欢
      • 2012-07-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-03
      相关资源
      最近更新 更多