【问题标题】:How does one deal with various errors in statically typed languages (or when typing in general)如何处理静态类型语言(或一般类型)中的各种错误
【发布时间】:2020-03-10 18:37:57
【问题描述】:

就上下文而言,我的主要语言是 Python,而且我刚刚开始使用注释。这是为学习 C++ 做准备(因为直觉上感觉更好)。


我有这样的事情:

from models import UserLocation
from typing import Optional
import cluster_module
import db
def get_user_location(user_id: int, data: list) -> Optional[UserLocation]:
    loc = UserLocation.query.filter_by(user_id=user_id).one_or_none()
    if loc:
        return loc
    try:
        clusters = cluster_module.cluster(data)
    except ValueError:
        return None # cluster throws an error if there is not enough data to cluster

    if list(clusters.keys()) == [-1]:
        return None # If there is enough data to cluster, the cluster with an index of -1 represents all data that didn't fit into a cluster. It's possible for NO data to fit into a cluster.
    loc = UserLocation(user_id=user_id, location = clusters[0].center)
    db.session.add(loc)
    db.session.commit()
    return loc

所以,我使用typing.Optional 来确保在出现错误的情况下可以返回None(如果我理解正确的话,与此等效的静态类型语言将返回适当类型的空指针)。但是,如何区分这两个错误?例如,如果没有足够的数据进行聚类,我想做的是返回-1,如果有数据,则返回-2,但它们都不适合集群(或类似的东西)。在 Python 中,这很容易(因为它不是静态类型的)。即使是mypy,我也可以说类似typing.Union[UserLocation, int]

但是,如何在 C++ 或 Java 中做到这一点? Java 程序员是否需要设置函数以返回int,并返回UserLocation 的ID 而不是对象本身(然后,任何使用get_user_location 函数的代码本身都会进行查找)?这样做有运行时的好处,还是只是重构代码以适应语言是静态类型的事实?

我相信我了解静态类型 w.r.t 的大部分明显好处。代码可读性、编译时间和运行时的效率——但我不确定如何处理这个特定问题。

简而言之:如何处理函数(返回非基本类型)表明它们在静态类型语言中遇到了不同的错误?

【问题讨论】:

  • 各种方式(~“任何可行的方式”)。有关许多 C++ 方法,请参阅stackoverflow.com/questions/3157098/…
  • 听起来你需要异常而不是返回值。
  • @FrançoisAndrieux 在一个函数预计会因多种情况而失败的情况下,如何区分不同的情况/失败? (这是std::variant会被使用的情况吗?)
  • @FrançoisAndrieux 啊!优秀的。谢谢你。 :) 如果您将您的 cmets 放入答案中,我会很乐意接受。
  • 我应该提一下,您不知何故忘记了将异常作为错误处理机制之一。返回某种std::variantstd::optional 意味着调用者负责解包结果并检查通常不需要的有效值或报告的错误数据。此外,您可能应该将 Ada / Spark 视为正确的静态类型语言。 C/C++ 比较宽松。

标签: python c++ typing static-typing


【解决方案1】:

与 python 解决方案等效的直接 C++ 将是 std::variant<T, U>,其中 T 是预期的返回值,U 是错误代码类型。然后,您可以检查变体包含哪些类型并从那里开始。例如:

#include <cstdlib>
#include <iostream>
#include <string>
#include <variant>

using t_error_code = int;

// Might return either `std::string` OR `t_error_code`
std::variant<std::string, t_error_code> foo()
{
    // This would cause a `t_error_code` to be returned
    //return 10;

    // This causes an `std::string` to be returned
    return "Hello, World!";
}

int main()
{
    auto result = foo();

    // Similar to the Python `if isinstance(result, t_error_code)`
    if (std::holds_alternative<t_error_code>(result))
    {
        const auto error_code = std::get<t_error_code>(result);
        std::cout << "error " << error_code << std::endl;
        return EXIT_FAILURE;
    }

    std::cout << std::get<std::string>(result) << std::endl;
}

但是,这在实践中并不常见。如果一个函数预计会失败,那么像 nullptrend 迭代器这样的单个失败返回值就足够了。此类失败是意料之中的,并不是错误。如果失败是意外的,则首选异常,这也消除了您在此处描述的问题。既期待失败又关心失败发生原因的细节是不寻常的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-25
    • 1970-01-01
    • 2021-11-16
    • 1970-01-01
    • 2016-09-26
    相关资源
    最近更新 更多