Go 語言中的範圍

來源:互聯網
上載者:User
理解 Go 語言中的範圍是怎麼起作用的,需要一些關於塊的預備知識,這在 “[Go 語言中的代碼塊](https://studygolang.com/articles/12632)” 文章中有講。一個標識符的範圍是標識符與某個值,比如變數、常量、包等,進行綁定的那一部分源碼(有時甚至是全部)。```gopackage mainimport "fmt"func main() { { v := 1 { fmt.Println(v) } fmt.Println(v) } // 編譯錯誤:“undefined: v” // fmt.Println(v)}```對於有經驗的工程師,很容易就能判斷出程式的輸出應該是這樣的:```> ./bin/sandbox11```最後一行的 fmt.Println 被注釋了,因為它會引起編譯錯誤。很快我們就來解釋這是為什麼。簡單的說,變數 v 在包含定義它代碼塊的大括弧之外,就超出它的範圍了。值得一提的是,給變數賦一個新的值並不影響它的範圍(也叫可見度):```gov := 1{ v = 2 // 賦值 fmt.Println(v)}fmt.Println(v)```輸出:```>./bin/sandbox22```而且它與下面的代碼運行結果不同:```gov := 1{ v := 2 // 簡短變數聲明方式 fmt.Println(v)}fmt.Println(v)```這段代碼的輸出是:```>./bin/sandbox21```範圍與標識符的定義緊密相關(更準確的說是與標識符被聲明的地方)## 變數或者常量的聲明變數標識符的範圍能到達最內層的代碼塊(不論是隱含的或者是顯式用大括弧包圍起來的):```gofunc main() { { v := 1 { fmt.Println(v) { fmt.Println(v) } } fmt.Println(v) }}```這段代碼是 100% 有效代碼,運行結果為:```> ./bin/sandbox111```範圍從變數被聲明的那一行代碼開始。```gofunc main() { fmt.Println(v) v := 1}```所以這段代碼會拋出一個編譯錯誤 “undefined: v”。簡短變數聲明方式可以一次性聲明多個變數:```goa, b := 0, 1```但是標識符從它被聲明語句結束的地方開始有效,所以,下面這一句是錯的:```goa, b := 1, a // 未定義的: a```對於簡短變數聲明,上述範圍規則同樣適用:- 變數聲明(使用 var 關鍵字)- 常量聲明(使用 const 聲明)在括起來的變數或常量聲明中,變數或者常量從它們被聲明的語句之後就生效,而不需要等整個括起來的代碼結束,所以下面的代碼是有效:```govar ( a = 1 // 變數聲明 no. 1 b = a // 變數聲明 no. 2)fmt.Println(a, b)```運行結果是:```> ./bin/sandbox1 1```同樣的,如果在括起來的聲明中,如果用簡短方式聲明多個變數,```govar ( a, b = 1, a)```這樣的代碼同樣會報編譯錯誤 —— “undefined: a”## 型別宣告就範圍而言,型別宣告與變數或者常量一樣 —— 一直作用到最內層的代碼塊。但是與變數或常量不同的是,型別宣告從標識結束的地方就開始生效了,而不是從類型定義代碼結束的地方才生效。這一點額外的“空間”讓類型遞迴稱為可能:```gotype X struct { name string next *X}x := X{name: "foo", next: &X{name: "bar"}}fmt.Println(x.name)fmt.Println(x.next.name)fmt.Println(x.next.next)```輸出:```> ./bin/sandboxfoobar<nil>```next 欄位必須是一個指標,下面這樣的定義是不合法的:```gotype X struct { name string next X}```因為編譯器會拋出 “invalid recursive type X” 的錯誤,產生這個錯誤的原因是,當建立類型 X 時,要計算這個類型的大小,而編譯器發現類型 X 的 next 欄位也是 X 類型,一個同樣的還沒有確定大小的欄位,於是我們會陷入一個無窮遞迴中。但是如果是一個指標類,編譯器就能知道在指定平台上指標類型的確定大小。## 預定義標識符有很多內建的標識符:- 類型:bool, int32, int64, float64, …- nil- 函數: make, new, panic, …- 常量,比如 true/false它們有全域的範圍,所以它們可以在代碼的任何地方使用。## Imports當匯入包時,包內名稱的範圍就是在檔案塊內。這樣包內的標識符只能在包已經被正確匯入後,通過 f.ex 的方式來引用。```go// sandbox.gopackage mainimport "fmt"func main() { fmt.Println("main") f()}// utils.gopackage mainfunc f() { fmt.Println("f")}```當編譯以上包時,編譯器會拋出錯誤:“undefined: fmt in fmt.Println”。## 頂級的標識符在任何函數外聲明的變數,常量,類型,函數,在整個包內是可見的(範圍是整個包)```go// sandbox.gopackage mainfunc main() { f()}// utils.gopackage mainimport "fmt"func f() { fmt.Println("It works!")}```以上代碼可以編譯並運行輸出:```> ./bin/sandboxIt works!```## 函數和方法方法的調用者,函數參數 或者 傳回值僅僅在函數體內可以訪問 —— 這個顯而易見的就不用代碼示範了。## 遮蔽(Shadowing)在同一個代碼塊中,一個標識符不能被聲明兩次。但是在內部的代碼塊中可以重新聲明外部被聲明了的標識符(代碼塊可以像洋蔥那樣一層層嵌套的)。如果在內層重新聲明了標識符,那麼在代碼中起作用的聲明是離代碼最近的最裡層的那個聲明:```gov := "outer"fmt.Println(v){ v := "inner" fmt.Println(v) { fmt.Println(v) }}{ fmt.Println(v)}fmt.Println(v)```輸出:```> ./bin/sandboxouterinnerinnerouterouter```## 參考資料- [《Go語言規範》](https://golang.org/ref/spec#Declarations_and_scope)- [《Go 語言中的代碼塊》](https://studygolang.com/articles/12632)

via: https://medium.com/golangspec/scopes-in-go-a6042bb4298c

作者:Michał Łowicki 譯者:MoodWu 校對:polaris1119

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽

257 次點擊  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.