以下函数进行一系列系统调用以放大映射文件缓冲区:
func (file *File) Ensure(more int) (err error) {
if file.Append+more <= cap(file.Buf) {
return
}
// there is not enough room
if err = syscall.Munmap(file.Buf); err != nil {
return
}
if _, err = file.Fh.Seek(0, os.SEEK_END); err != nil {
return
}
if _, err = file.Fh.Write(make([]byte, file.Growth)); err != nil {
return
}
if err = file.Fh.Sync(); err != nil {
return
}
if file.Buf, err = syscall.Mmap(int(file.Fh.Fd()), 0, cap(file.Buf)+file.Growth, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED); err != nil {
return
}
return
}
5行中有5个系统调用,并且有 11 行错误处理代码。有更清洁的方法吗?
答案 0 :(得分:1)
这取决于您对清洁度的定义。有些人认为,与异常相比,处理错误的地方是干净的。请注意您在每个错误路径中如何使用除默认操作(返回错误)之外的代码中的干净点。它可以是尝试恢复(在适用的地方),它可以用更多细节来修改错误,例如关于五个调用中的哪一个失败的人类可读标签。当一个人意识到很容易得到一些相同的东西时,这实际上是一个好主意,比如绝对不止一个系统调用的EPERM或EINVAL。
另一个观察结果是:如何消除错误(如果假设它更清晰),错误检测(if err != nil
)和错误操作(在这种情况下为return
)?< / p>
答案 1 :(得分:1)
在您的示例中,错误检查模式不太明显。将它与JVM和类似语言进行比较,每个调用都会抛出一个异常,并且该模式需要远更少的行,而且要少得多。
然而(并且它是一个很大'但是'),在其他情况下,您可能需要以不同于其他错误的方式处理每个错误情况。在Go风格中,这很容易。在异常风格中,它不是;相反,需要很多尝试捕获块,所需的仪式数量将被颠倒。
所以我的结论是,我觉得Go模式有点苦差事,但我很欣赏有时它是一件好事,可以弥补这些苦差事。我也发现自己在启动代码中经常使用panic
,所以我不需要从基本上无法恢复的东西中恢复。
作为最后的观察,函数式编程专家(例如在Scala中)就像所谓的Either
模式,其中函数返回结果或错误;这优先于使用异常。它与Go的真实模式相同。