正常情况
在编写具有命名返回值的函数时,通常可以使用裸返回(是否应该单独讨论)。它们可能看起来如下:
func add(x, y int) (z int) {
z = x + y
return
}
此处的return
与return z
含义相同
有问题的情况
然而,下面的节略片段。。。
func loadModule(moduleName, fileRoot string) (module []byte) {
if strings.HasSuffix(moduleName, ".md") {
module, err := readConvertMDFile(fileRoot + "htdocs/md/" + moduleName)
if err != nil {
log.Println(err)
}
return module
} else {
module = []byte{}
return
}
}
(这个片段运行良好,是我目前解决问题的方法)
如果只有CCD_ 5而不是CCD_。这是因为module
是(第二次)与err
一起声明的,必须声明CCD_7,因为它在这个范围内还不存在。
可能的解决方案
按照我所做的操作,显式命名返回变量。虽然这不是一个糟糕的解决方案,但我觉得应该有一种方法来安排代码,使其以裸返回的方式正常运行。其他人评论说,这种显式的返回会导致"代码气味"。
在开头添加
var err error
,并使用多重赋值而不是声明。可能是一个更好的解决方案,但为了一致性和减少不必要的行,我更喜欢在可能的情况下使用隐式赋值。使用临时
moduleT
变量,然后分配module = moduleT
。。。这让人觉得既混乱又多余。
虽然我可以得到我想要的编译结果,但我希望有人能提出一种清晰、惯用的写作方式。
我总是使用您建议的解决方案2-添加一个额外的var
语句。
func loadModule(moduleName, fileRoot string) (module []byte) {
var err error
if strings.HasSuffix(moduleName, ".md") {
module, err = readConvertMDFile(fileRoot + "htdocs/md/" + moduleName)
if err != nil {
log.Println(err)
}
return
} else {
// no need for this as module will be nil if it isn't written to
// a nil slice is perfectly legal and has len()=0
// and can be appended to etc
// module = []byte{}
return
}
}
选项2也是最有效的解决方案。请记住,go返回堆栈上的所有值,因此命名的返回值相当于堆栈分配的变量。
如果在选项1或选项3中没有裸返回,那么其中无论如何都有一个隐含的module = module
或module = moduleT
语句。
不幸的是,变量阴影会在一段时间后折磨每个Go程序员。我非常希望编译器不允许函数中的所有阴影,因为它是真正错误的来源。
在我写这个问题时,我的函数如下所示:
(主要用于展示其冗长性)
func loadModule(moduleName, fileRoot string) (module []byte) {
if strings.HasSuffix(moduleName, ".md") {
module, err := readConvertMDFile(fileRoot + "htdocs/md/" + moduleName)
if err != nil {
log.Println(err)
}
return module
} else if strings.HasSuffix(moduleName, ".html") {
module, err := ioutil.ReadFile(fileRoot + "htdocs/html/" + moduleName)
if err != nil {
log.Println(err)
}
return module
} else if strings.HasSuffix(moduleName, ".js") {
module, err := ioutil.ReadFile(fileRoot + "htdocs/js/" + moduleName)
if err != nil {
log.Println(err)
}
return module
} else if strings.HasSuffix(moduleName, ".css") {
module, err := ioutil.ReadFile(fileRoot + "htdocs/css/" + moduleName)
if err != nil {
log.Println(err)
}
return module
} else {
module = []byte{}
return
}
}
这使用了我建议的解决方案1。它有很多重复的代码(我还是个初学者)。如果我改为使用建议的解决方案2(但不是按照我最初想到的方式),将var err error
放在then函数的顶部,代码将从两个方面得到改进:
func loadModule(moduleName, fileRoot string) (module []byte) {
var err error
switch {
case strings.HasSuffix(moduleName, ".md"):
module, err = readConvertMDFile(fileRoot + "htdocs/md/" + moduleName)
case strings.HasSuffix(moduleName, ".html"):
module, err = ioutil.ReadFile(fileRoot + "htdocs/html/" + moduleName)
case strings.HasSuffix(moduleName, ".js"):
module, err = ioutil.ReadFile(fileRoot + "htdocs/js/" + moduleName)
case strings.HasSuffix(moduleName, ".css"):
module, err = ioutil.ReadFile(fileRoot + "htdocs/css/" + moduleName)
default:
module = []byte{}
}
if err != nil {
log.Println(err)
}
return
}
不再有阴影变量,错误日志和返回都可以从每个if语句中移出,从而生成更清晰的代码。
很可能有办法改进这一点。编辑:。。。根据@ANisus的建议,if-else链已被switch语句替换。