如何处理静态类型语言中的各种错误(或一般输入时)
How does one deal with various errors in statically typed languages (or when typing in general)
对于上下文,我的主要语言是 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
来确保我可以 return None
以防出现错误(如果我理解正确的话,静态类型语言等价于此是 return 适当类型的空指针)。但是,如何区分这两个错误呢?例如,我想做的是 return -1
如果没有足够的数据来聚类, -2
如果有数据,但是 none 它们适合集群(或类似的东西)。在 Python 中,这很容易(因为它不是静态类型的)。即使 mypy
,我也可以说 typing.Union[UserLocation, int]
。
但是,如何在 C++ 或 Java 中做到这一点? Java 程序员是否需要做一些事情,比如将函数设置为 return int
,并且 return UserLocation
的 ID 而不是对象本身(然后,任何使用 get_user_location
函数的代码本身都会进行查找)?这样做对运行时有好处吗,还是只是重构代码以适应语言是静态类型的事实?
我相信我了解静态类型的大部分明显好处 w.r.t。代码可读性、编译时间和运行时效率——但我不确定如何处理这个特定问题。
简而言之:如何处理表明它们 运行 在静态类型语言中出现不同错误的函数(return 非基本类型)?
与 python 解决方案等效的直接 C++ 是 std::variant<T, U>
,其中 T
是预期的 return 值,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;
}
然而,这在实践中并不常见。如果一个函数预计会失败,那么单个失败的 return 值(如 nullptr
或 end
迭代器就足够了。这种失败是预料之中的,不是错误。如果失败是意外的,则首选异常,这也消除了您在此处描述的问题。既期望失败又关心失败原因的细节是不寻常的。
对于上下文,我的主要语言是 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
来确保我可以 return None
以防出现错误(如果我理解正确的话,静态类型语言等价于此是 return 适当类型的空指针)。但是,如何区分这两个错误呢?例如,我想做的是 return -1
如果没有足够的数据来聚类, -2
如果有数据,但是 none 它们适合集群(或类似的东西)。在 Python 中,这很容易(因为它不是静态类型的)。即使 mypy
,我也可以说 typing.Union[UserLocation, int]
。
但是,如何在 C++ 或 Java 中做到这一点? Java 程序员是否需要做一些事情,比如将函数设置为 return int
,并且 return UserLocation
的 ID 而不是对象本身(然后,任何使用 get_user_location
函数的代码本身都会进行查找)?这样做对运行时有好处吗,还是只是重构代码以适应语言是静态类型的事实?
我相信我了解静态类型的大部分明显好处 w.r.t。代码可读性、编译时间和运行时效率——但我不确定如何处理这个特定问题。
简而言之:如何处理表明它们 运行 在静态类型语言中出现不同错误的函数(return 非基本类型)?
与 python 解决方案等效的直接 C++ 是 std::variant<T, U>
,其中 T
是预期的 return 值,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;
}
然而,这在实践中并不常见。如果一个函数预计会失败,那么单个失败的 return 值(如 nullptr
或 end
迭代器就足够了。这种失败是预料之中的,不是错误。如果失败是意外的,则首选异常,这也消除了您在此处描述的问题。既期望失败又关心失败原因的细节是不寻常的。