Go中的递归函数,如果内层函数returns,外层函数是否继续正常执行?

With recursive functions in Go, if the inner function returns, does the outer function continue execution normally?

好的,所以我有这段代码

func registerDomain(domainName string, n int) bool {
    //building the request here 
    resp, errr := client.Do(r)
    if errr != nil { 
        if n == 1 {
            return false
        }
        registerDomain(domainName, n-1)
    }

    bodyBytes, err2 := ioutil.ReadAll(resp.Body)
    if err2 == nil {
        resp.Body.Close()
        //handle bodyBytes
        //if the response is how it should be return true, if it's not call the function again with n-1
    } else { //if there is an error reading the response
        resp.Body.Close()
        if n == 1 {
            return false
        }
        registerDomain(domainName, n-1)
    }
    return false //it should never reach this line
}

解释:

我使用参数 n(假设为 5)调用该函数,该参数表示函数在出现问题时重试的次数。每次出现问题时,我都会用 n-1 进行递归调用,所以当它到达 n=1 时,它会放弃并且 returns false。这段代码在实践中运行良好,它做了它应该做的事情,有时当响应不正确时它递归地调用自己并且它在第二次(或第三次,第四次......)时工作。如果问题在 n=1 之前未解决,则 returns 错误。

问题:

这是一个大代码的一部分,它应该 运行 大约 17 个小时,当它试图从正文中读取时,它有时会在这一行出现恐慌:

bodyBytes, err2 := ioutil.ReadAll(resp.Body)

它说恐慌:运行时间错误:无效的内存地址或零指针取消引用。现在我知道这可能意味着它试图从一个不存在的 resp.Body 中读取,但是 Go documentation 明确指出 当 err 为 nil 时,resp 总是包含一个非 nil resp.Body

所以,在我看来,这可能与递归调用有关。 only 对我来说有意义的是这种情况:可以说 errr 不是零(这意味着 resp.Body 不存在),所以它进入了内部的 if errr != nil 并且因为 n!=1,它会再次用 n=4 调用自己。假设这次一切都应该是这样,第二个函数 returns 对第一个函数 但是 第一个函数继续执行并尝试从 resp.Body 不存在。这会引起恐慌,我们在这里...

所以,我需要的是一个确切知道递归函数如何工作的人,如果不是,我可以在阅读之前以某种方式检查 resp.Body 是否存在,或者有帮助的东西。

总之谢谢! :)

更新: 你没事,我的代码不再恐慌了,我也没有。非常感谢! (我不确定这是不是更新的地方)

您需要在第一次调用 registerDomain 后 return ,否则当您对 registerDomain 的调用完成时 ioutil.ReadAll 会出现恐慌

//building the request here 
resp, errr := client.Do(r)
if errr != nil { 
    if n == 1 {
        return false
    }
    return registerDomain(domainName, n-1)
}

改变

registerDomain(domainName, n-1)

return registerDomain(domainName, n-1)

这样外部函数将不会继续执行,也不会从 nil 主体中读取。

registerDomain() 函数中有 2 个地方可以调用它自己。

如果client.Do()失败(errr != nil),您将再次调用registerDomain()。有时它会 return,当它发生时,您的代码执行将继续,尝试从 resp.Body 读取,但最有可能是 nil 因为 errr != nil.

以下是您应该如何处理它:

每当你进行递归调用时,你不应该让代码在调用 returns 时继续,而是 return 它 returned 的值,像这样:

return registerDomain(domainName, n-1)

备选

您尝试解决的问题可以使用 for 语句在不递归的情况下解决。 for变体甚至会更清晰、更简单。

将你的 registerDomain() 函数修改为 return false 在它调用自身的每个点上,并使用此循环重试 5 次:

var result bool
for i := 0; i < 5; i++ {
    result = registerDomain(domainName) // You don't need to pass n anymore
    if result {
        break
    }
}

if result {
    fmt.Println("Domain registered successfully!")
} else {
    fmt.Println("Failed to register domain!")
}