arrays - Golang 最佳实践:空数组响应或错误?
问题描述
对于接受对象切片并返回另一个对象切片(理想情况下与输入数组长度相同)以及错误的函数,在错误处理方面的最佳实践如下:
func ([]interface{}) ([]interface{}, error)
一种方法是,每当您在处理切片中的一个对象时遇到错误时,您都会返回错误响应,但是在接收函数中,如果您不丢弃所有切片元素,错误响应就变得毫无用处,只是告诉我们认为处理其中一个元素或全部失败。另一种方法是在没有处理任何元素时返回错误,但我觉得这又没什么用。另一种方法是您不包含错误作为返回对象,而是对于每个切片元素结构,将其自己的错误对象作为复合对象,以便您可以将元素错误作为输出发送。
最好的方法显然取决于特定的场景,但是,我想知道人们是否遵循任何最佳实践或围绕这个问题的任何设计模式。
PS:这是最接近的问题之一,但是因为它接受单个对象作为输入,所以不是很相关: 返回空数组或错误
解决方案
...一个函数,接受[表示一个]对象数组的接口切片并返回另一个[表示一个]对象数组的[接口切片]以及错误...
你没有告诉我们足够多的事情继续下去。
- 返回的切片实际上与参数切片有什么关系吗?
- 如果有,他们有什么关系?例如,返回的切片可能应该是输入切片大小的一半,当且仅当输入对象的数量为奇数时才会发生错误,在这种情况下,最后一个输入对象已被忽略。
- 输入必须按顺序处理,还是并行处理?
另一种方法是您不包含错误作为返回对象,而是对于每个数组对象结构,将其自己的错误对象作为复合对象,以便您可以将元素错误作为输出发送。
如果输出与输入是一对一的,并且您打算并行处理它们和/或在达到一个坏的输入时继续处理剩余的输入,这可能是一种明智的方法。等效地,您可以让输出切片包含错误。
这真的非常依赖问题。
编辑:考虑,例如,以下(我不认为这是好的,请注意):
const maxWorkers = 10 // tunable
// Process a slice of T's in parallel. The results are either an
// R for each T, or an error. Caller provides the actual function
// f(T), which returns R + error (an empty/zero R for error).
func ProcessInParallel(input []T, f func(T) (R, error)) ([]interface{}, error) {
// Make one channel for sending work to workers,
// and one for receiving results from workers.
type Todo struct {
i int // the index of the item
item T // the item to work on
}
workChan := make(chan Todo)
type Done struct {
i int // the index of the item worked on
r R // result, if we have one
e error // error, if we have one
}
doneChan := make(chan Done)
// Spin off workers: maxWorkers or len(input),
// whichever is smaller.
n := len(input)
if n > maxWorkers {
n = maxWorkers
}
var wg sync.WaitGroup
for i := 0; i < n; i++ {
wg.Add(1)
go func(i int) {
for todo := range workChan {
i := todo.i
r, err := f(input[i])
doneChan <- Done{i, r, err}
}
wg.Done()
}(i)
}
// Close doneChan when all workers finish.
go func() {
wg.Wait()
close(doneChan)
}()
// Hand out work to workers (then close work channel).
go func() {
for i := range input {
workChan <- Todo{i, input[i]}
}
close(workChan)
}()
// Collect results.
var anyErr error
ret := make([]interface{}, len(input))
for done := range doneChan {
i := done.i
r, err := done.r, done.e
if err != nil {
anyErr = err
ret[i] = err
} else {
ret[i] = r
}
}
return ret, anyErr
}
这有一个整体错误返回,它返回一个interface{}
. 这意味着您可以立即判断一切是否正常。但是,使用起来有点烦人:
ret, err := ProcessInParallel(arg, f)
if err != nil {
fmt.Println("some inputs failed")
for i := range ret {
if e, ok := ret[i].(error); ok {
fmt.Printf("%d: failed: %v\n", i, e)
} else {
fmt.Printf("%d: %s\n", i, ret[i].(R))
}
}
} else {
fmt.Println("all inputs were good")
for i := range ret {
fmt.Printf("%d: %s\n", i, ret[i].(R))
}
}
为什么要打扰所有错误摘要?
相反,我们可以使用ProcessInParallel
return []R, []error
,例如,或者——可能更好——使用一个简单的error
接口返回值来存储MultiError,正如Cerise Limón 在评论中建议的那样:
ret, err := ProcessInParallel(arg, f)
if err != nil {
if merr, ok := err.(datastore.MultiError); ok {
// merr[i] indicates the various failed items
// any ret[i] for which merr[i] is nil is OK
}
} else {
// all ret[i] are ok
}
一个不使用的工作示例MultiError
是here。
一个确实使用的工作示例MultiError
是here。
推荐阅读
- powerbi - 设置过滤器的开始值和结束值
- react-native - 解决没有为提供的活动注册的本机启动画面
- php - PHP 中的 Geotiff 操作
- mysql - CASE..ELSE 将为未使用的表达式占用额外的内存
- javascript - 根据模型/接口限制对象数组的对象值?
- javascript - 表单提交结束时的 Mvc 核心隐藏加载器
- laravel - cPanel 上的背包 Laravel:后端配置无效。可读卷不可用
- php - 需要在 CURLOPT_POSTFIELDS 中传递动态值
- reactjs - 加载 ag-grid 服务器端模型时需要应用以前保存的过滤器/排序模型
- python - 我正在尝试在报告的表格中正确地创建一个块行