ホームページ  >  記事  >  バックエンド開発  >  この Go の問題を正しく理解できますか? 8割以上の人が勘違いしてる…

この Go の問題を正しく理解できますか? 8割以上の人が勘違いしてる…

Golang菜鸟
Golang菜鸟転載
2023-08-08 16:35:02645ブラウズ


最近サーフィンをしているときに、Redhat のチーフ ソフトウェア エンジニアであり、Prometheus やその他のプロジェクトのメンテナである Bartłomiej Płotka が Twitter に投稿しているのを目にしました。 Goの「試験問題」は全員をテストするためのものです。

この Go の問題を正しく理解できますか? 8割以上の人が勘違いしてる…

質問は次のとおりです:

func aaa() (done func(), err error) {
 return func() { 
   print("aaa: done") 
 }, nil
}

func bbb() (done func(), _ error) {
 done, err := aaa()
 return func() { 
   print("bbb: surprise!"); 
   done() 
 }, err
}

func main() {
 done, _ := bbb()
 done()
}

回答オプションは次のとおりです:

Go テストの質問 (単一選択肢)



  • ##C: 出力を続け、終わらない# ########################################


    想想原因,思考一下输出结果是什么?是 A,还是 D?还是三短一长,选 B?

    分析程序

    缩小范围,核心关注到这块代码。如下:

    func bbb() (done func(), _ error) {
     done, err := aaa()
     return func() { 
       print("bbb: surprise!")
       done() 
     }, err
    }

    在最后一行的这个闭包(匿名函数)中,大家可能认为程序调用了函数 aaa 所返回的 done 值来输出程序,应当是:

    aaa: done

    这个想法是错误的,程序没有这么去运作。

    原因在于 return 实际上是一个赋值语句。结合程序,可以看到函数 bbb 的第一个返回值是 done 参数。

    如下:

    func bbb() (done func(), _ error)

    也就是在函数 bbb 在程序最后执行 return 语句后,会对返回变量 done 进行赋值,自然该值不会是由函数 aaa 所设置的了。

    这是一个关键的地方。

    具体过程

    这个程序输出结果是什么呢?

    会不断地递归,疯狂输出 “bbb: surprise!”,直至栈溢出,导致程序运行出错,最终中止

    同学就疑惑了,怎么又多出了个递归?

    我们再看看程序:

    func main() {
     done, _ := bbb()
     done()
    }
    
    func bbb() (done func(), _ error) {
     ...
     return func() { 
       print("bbb: surprise!"); 
       done() 
     }, err
    }

    本质上在函数 bbb 执行完毕后, 变量 done 已经变成了一个递归函数。

    递归的过程是:函数 bbb 调用变量 done 后,会输出 bbb: surprise! 字符串,然后又调用变量 done。而变量 done 又是这个闭包(匿名函数),从而实现不断递归调用和输出。

    最终结果如下:

    b: surprise!bbb: surprise!bbb: surprise!runtime: goroutine stack exceeds 1000000000-byte limit
    runtime: sp=0xc0200e0380 stack=[0xc0200e0000, 0xc0400e0000]
    fatal error: stack overflow
    
    runtime stack:
    runtime.throw(0x1074b5a, 0xe)
            /usr/local/Cellar/go/1.16.6/libexec/src/runtime/panic.go:1117 +0x72
    runtime.newstack()
            /usr/local/Cellar/go/1.16.6/libexec/src/runtime/stack.go:1069 +0x7ed
    runtime.morestack()
            /usr/local/Cellar/go/1.16.6/libexec/src/runtime/asm_amd64.s:458 +0x8f
    ...

    也就是正确答案是:D,程序最终运行出错。

    一直调用一直爽,直至栈溢出程序崩溃。

    总结

    这位大佬出的题目,本质上是比较烦人的,其结合了函数返回参数的命名用法。

    如果我们把这个函数的返回参数命名去掉,就可以避开这个问题。如下:

    func bbb() (func(), error) {
     ...
     return func() { 
       print("bbb: surprise!"); 
       done() 
     }, err
    }
    ...

    输出结果为 "bbb: surprise!"。

    很多 Go 的同学在日常代码编写的时候不会用到或注意到。但如果写的时候有类似案例代码中的模式,就会排查许久都查不到。

    这是个有警惕意义的题目,你觉得呢?

以上がこの Go の問題を正しく理解できますか? 8割以上の人が勘違いしてる…の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はGolang菜鸟で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。