【问题标题】:Scala's Option or Rust's Result error handling in C [closed]C中Scala的选项或Rust的结果错误处理[关闭]
【发布时间】:2020-07-25 11:11:58
【问题描述】:

在专业从事 Scala 和 Python 工作一段时间后,我再次开始编写 C。 在使用 Scala 的“Option”和“Either”错误处理模式并最近尝试了 Rust 方式之后,我想要在 C 中使用类似的东西。所以我想出了这样的东西:

typedef struct {
    int age;
    char* name;
} User;

typedef struct {
    char* error;
    User* value;
} UserResult;

UserResult get_user() {
    /* Some complicated user fetching process .. that fails */
    return (UserResult) { .error = "403 Unauthorized\n" };
}

int main(void) {
    UserResult res = get_user();
    if (res.error)
        handle_error(res.error);
    if (res.value)
        do_something(res.value);
    /* ... */
    return 0;
}

但这并不安全(我们可以访问无效指针)。我怎样才能得到类似于 Scala 或 Rust 处理 C 中错误的方式?

编辑:更正 UserResult 字段名称“值”

【问题讨论】:

    标签: c scala error-handling rust


    【解决方案1】:

    有很多方法可以处理现有 C 代码中的错误。如此之多,以至于理解如何处理各种库的错误是一种不可忽视的精神负担,在一个项目中同时使用多个库时更是如此。

    引入一种处理错误的新方法可能是有益的,但另一方面,它可能类似于this famous XKCD comic

    Rust(和 Scala 等)方式确实非常强大,并且与其他较旧的习惯用法相比具有一些优势。

    但是,Result 惯用语是基于Sum Types,C 语言本身并不支持。 无法保证您的 UserResult 处于一致状态。 usererror 可以同时为 null 或非 null。

    这可以稍作修改,例如使用联合:

    typedef struct{
        union {
            char* error;
            User user;
        }
        bool has_error;
    } UserResult;
    

    has_error 标志指示实际填充的是哪个字段:

    int main(void) {
        UserResult res = get_user();
        if (res.has_error)
            handle_error(res.error);
        else
            do_something(res.value);
        /* ... */
        return 0;
    }
    

    这并不完美,因为 C 语言不强制执行正确的联合访问,而是在使用不正确时调用 undefined behaviour

    【讨论】:

      【解决方案2】:

      问题是即使在 C 中使用 UserResult 模型,程序员仍有责任记住进行空检查,也就是说,编译器没有强制执行它,所以即使我们声明了 UserResult,也没有什么能阻止我们写错了

      int main(void) {
        UserResult res = get_user();
        do_something(res.value); // Runtime error: oops I forgot to null-check and unfortunately compiler is still happy
        return 0;
      }
      

      而 Scala 的 Option 程序员是被编译器强制处理的情况,例如,

      case class User(age: Int, name: String)
      case class UserResult(value: Option[User], error: Option[String])
      
      def get_user(): UserResult = {
        /* Some complicated user fetching process .. that fails */
        UserResult(value = None, error = Some("403 Unauthorized"))
      }
      
      def do_something(user: User) = ???
      
      object Main extends App {
        val res = get_user()
        do_something(res.value) // Compile-time error: oops I forgot to "null-check" but fortunately the compiler is unhappy
      }
      

      【讨论】:

        猜你喜欢
        • 2018-10-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-06
        • 2016-07-10
        • 2020-04-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多