Go - 返回(潜在阴影)变量的惯用方法,这些变量已被定义为单独的多变量返回的一部分

azm*_*zmr 5 variables return-value go

正常情况

在编写具有命名返回值的函数时,通常可以使用裸返回(无论您是否应该单独讨论).它们可能类似于以下内容:

func add(x, y int) (z int) {
    z = x + y
    return
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

(这个片段运行正常,是我目前解决问题的方法)

...编译器会抱怨,module is shadowed如果不是return module只有return.这是因为module已经声明(第二次)err,必须声明它在此范围内尚不存在.

可能的解决方案

  1. 像我一样做并明确命名返回变量.虽然这不是一个可怕的解决方案,但我觉得应该有一种方法来安排代码,以便它以裸露的回报运行.其他人评论说,这种明确的回报导致了"代码味".

  2. var err error在开头添加一个并使用多个赋值而不是声明.可能是一个更好的解决方案,但我更愿意在可能的情况下使用隐式赋值以保持一致性并减少不必要的行.

  3. 使用临时moduleT变量然后分配module = moduleT...这只是感觉凌乱和多余.

虽然我可以得到我正在寻找的编译结果,但我希望有人可以建议一种明确的,惯用的写作方式.

Nic*_*ood 5

我总是使用您建议的解决方案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
    }
}
Run Code Online (Sandbox Code Playgroud)

选项2也是最有效的解决方案。请记住,go返回堆栈上的所有值,因此命名的返回值等效于堆栈分配的变量。

如果在选项1或选项3中没有裸收益,那么无论如何都存在隐式module = modulemodule = moduleT语句。

不幸的是,可变阴影是一段时间后会困扰每个Go程序员的东西。我很希望编译器禁止函数内的所有阴影,因为它是真正的bug的来源。


azm*_*zmr 0

在我写问题时,我的函数如下所示:

(主要是为了展示其冗长性)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这使用了我建议的解决方案1。它有很多重复的代码(我还是个初学者)。var err error如果我通过将其放在 then 函数的顶部来使用建议的解决方案 2(但不是以我最初想到的方式) ,则可以通过两种方式改进代码:

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
}
Run Code Online (Sandbox Code Playgroud)

不再有隐藏变量,并且错误日志记录和返回都可以从每个 if 语句中移出,从而产生更清晰的代码。

很可能有一种方法可以改进这一点。编辑: ...并且,根据@ANisus的建议,if-else链已被switch语句替换。