如何在Go中使用變數和常量

變數是掌握編程的一個重要概念。它們是代表你在程序中使用的值的符號。

本教程將介紹一些變數的基本知識和在創建Go程序時使用它們的最佳實踐。

理解變數

從技術上講,變數是將一個存儲位置分配給一個與符號名稱或標識符相關聯的值。我們使用變數名稱來引用計算機程序中存儲的值。

我們可以將變數視為一個帶有名稱的標籤,你將其綁定到一個值上。

假設我們有一個整數,1032049348,我們希望將其存儲在一個變數中,而不是反覆重新輸入這個長數字。為了實現這一點,我們可以使用一個易於記憶的名稱,比如變數i。要將值存儲在變數中,我們使用以下語法:

i := 1032049348

我們可以將這個變數視為一個綁定到該值的標籤。

這個標籤上寫著變數名稱i,並綁定到整數值1032049348

短語i := 1032049348是一個聲明和賦值語句,由幾個部分組成:

  • 變數名稱(i
  • 短變數聲明賦值(:=
  • 變數名稱所綁定的值(1032049348
  • Go 推斷的數據類型(int

我們將在下一節看到如何明確設置類型。

這些部分共同構成了將變數 i 設置為整數 1032049348 值的語句。

一旦我們將變數設置為某個值,我們就初始化或創建了該變數。一旦完成,我們就可以使用變數來代替該值。

一旦我們將 i 設置為 1032049348 的值,我們就可以使用 i 來代替該整數,讓我們將其打印出來:

package main

import "fmt"

func main() {
	i := 1032049348
	fmt.Println(i)
}
Output
1032049348

我們也可以通過使用變數快速且輕鬆地進行數學運算。使用 i := 1032049348,我們可以用以下語法減去整數值 813

fmt.Println(i - 813)
Output
1032048535

在這個例子中,Go 為我們進行了數學運算,從變數 i 中減去 813,返回結果 1032048535

說到數學,變數可以設置為數學方程的結果。你也可以將兩個數字相加並將其和的值存儲到變數 x 中:

x := 76 + 145

你可能注意到這個例子看起來類似於代數。就像我們在公式和方程中使用字母和其他符號來表示數字和量一樣,變數是代表數據類型值的符號名稱。為了正確的 Go 語法,你需要確保變數在任何方程的左側。

讓我們繼續打印 x

package main

import "fmt"

func main() {
	x := 76 + 145
	fmt.Println(x)
}
Output
221

Go 返回了值 221,因為變數 x 被設定為 76145 的總和。

變數可以代表任何資料型別,不僅僅是整數:

s := "Hello, World!"
f := 45.06
b := 5 > 9 // 布林值將返回 true 或 false
array := [4]string{"item_1", "item_2", "item_3", "item_4"}
slice := []string{"one", "two", "three"}
m := map[string]string{"letter": "g", "number": "seven", "symbol": "&"}

如果你列印任何這些變數,Go 將返回該變數所等同的值。讓我們來處理字串 slice 資料型別的賦值語句:

package main

import "fmt"

func main() {
	slice := []string{"one", "two", "three"}
	fmt.Println(slice)
}
Output
[one two three]

我們將 []string{"one", "two", "three"} 的 slice 值賦給了變數 slice,然後使用 fmt.Println 函數透過呼叫 slice 來列印出該值。

變數的工作原理是,在你的電腦中劃出一小塊記憶體區域,接受指定的值,然後與該空間關聯。

宣告變數

在 Go 中,宣告變數有幾種方式,某些情況下,可以有多種方式宣告完全相同的變數和值。

我們可以宣告一個名為 iint 型別變數,而不進行初始化。這意味著我們將宣告一個放置值的空間,但不給予初始值:

var i int

這樣就建立了一個宣告為 iint 型別變數。

我們可以使用等號(=)運算符來初始化值,如下例所示:

var i int = 1

在 Go 中,這兩種聲明形式被稱為 長變數聲明

我們也可以使用 短變數聲明

i := 1

在這種情況下,我們有一個名為 i 的變數,其數據類型為 int。當我們不指定數據類型時,Go 會推斷數據類型。

通過這三種聲明變數的方式,Go 社群採用了以下慣例:

  • 僅在未初始化變數時使用長形式,var i int

  • 在聲明和初始化時使用短形式,i := 1

  • 如果您不希望 Go 推斷您的數據類型,但仍想使用短變數聲明,可以將您的值包裝在您期望的類型中,使用以下語法:

i := int64(1)

在 Go 中,當我們初始化值時,使用長變數聲明形式被認為是不符合慣例的:

var i int = 1

遵循 Go 社群通常聲明變數的方式是良好的實踐,以便其他人能無縫閱讀您的程序。

零值

所有內建類型都有一個零值。任何分配的變量即使從未賦值也是可用的。我們可以看到以下類型的零值:

package main

import "fmt"

func main() {
	var a int
	var b string
	var c float64
	var d bool

	fmt.Printf("var a %T = %+v\n", a, a)
	fmt.Printf("var b %T = %q\n", b, b)
	fmt.Printf("var c %T = %+v\n", c, c)
	fmt.Printf("var d %T = %+v\n\n", d, d)
}
Output
var a int = 0 var b string = "" var c float64 = 0 var d bool = false

我們在 fmt.Printf 語句中使用了 %T 動詞。這告訴函數打印變量的 數據類型

在 Go 中,因為所有值都有一個 零值,我們不能有像其他一些語言中的 未定義 值。例如,某些語言中的 布爾 可以是 未定義,這允許變量有三種狀態。在 Go 中,我們不能有超過 種狀態的布爾值。

變量命名:規則和風格

變量命名相當靈活,但有一些規則需要記住:

  • 變量名稱必須只是一個單詞(即沒有空格)。
  • 變量名稱必須由字母、數字和下劃線(_)組成。
  • 變量名稱不能以數字開頭。

遵循這些規則,讓我們來看一些有效和無效的變數名稱:

Valid Invalid Why Invalid
userName user-name Hyphens are not permitted
name4 4name Cannot begin with a number
user $user Cannot use symbols
userName user name Cannot be more than one word

此外,在命名變數時要記住它們是區分大小寫的。這些名稱 userNameUSERNAMEUserNameuSERnAME 都是完全不同的變數。在程式中避免使用相似的變數名稱是最佳實踐,以確保你和你的合作者——無論是現在還是未來——都能清楚地區分你的變數。

雖然變數是區分大小寫的,但變數首字母的大小寫在 Go 中有特殊含義。如果一個變數以大寫字母開頭,那麼該變數可以在聲明它的包之外訪問(或 導出)。如果一個變數以小寫字母開頭,那麼它只能在聲明它的包內部訪問。

var Email string
var password string

Email 以大寫字母開頭,可以被其他包訪問。password 以小寫字母開頭,只能在聲明它的包內部訪問。

在 Go 中,使用非常簡潔(或短)的變數名稱是很常見的。在選擇變數名稱時,如果可以在 userNameuser 之間選擇,通常會選擇 user

變數的作用域也會影響變數名稱的簡潔程度。規則是,變數存在的作用域越小,變數名稱就越短:

names := []string{"Mary", "John", "Bob", "Anna"}
for i, n := range names {
	fmt.Printf("index: %d = %q\n", i, n)
}

我們在更大的範圍內使用變數names,因此給它一個更有意義的名稱以幫助記住它在程式中的含義是很常見的。然而,我們在下一行代碼中立即使用變數in,然後不再使用它們…因此,這不會使閱讀代碼的人對變數的使用位置或含義感到困惑。

接下來,讓我們討論一些關於變數風格的注意事項。風格是使用MixedCapsmixedCaps而不是下劃線來命名多詞名稱。

Conventional Style Unconventional Style Why Unconventional
userName user_name Underscores are not conventional
i index prefer i over index as it is shorter
serveHTTP serveHttp acronyms should be capitalized

關於風格最重要的事情是保持一致性,並且你所在的團隊同意這種風格。

重新賦值變數

正如“變數”一詞所暗示的,我們可以輕鬆地改變Go變數。這意味著我們可以通過重新賦值將不同的值與先前賦值的變數關聯起來。能夠重新賦值是有用的,因為在程式的過程中,我們可能需要將用戶生成的值接受進已經初始化的變數中。我們也可能需要將賦值更改為先前定義的某個值。

知道我們可以輕鬆地重新賦值一個變數在處理別人編寫的大型程式時可能很有用,並且不清楚哪些變數已經被定義。

我們將值76賦給一個名為iint類型變數,然後再將其賦予新值42

package main

import "fmt"

func main() {
	i := 76
	fmt.Println(i)

	i = 42
	fmt.Println(i)
}
Output
76 42

此示例表明,我們可以首先將變數i賦予一個整數值,然後再次對變數i進行賦值,這次賦予其值42

注意:當您宣告並初始化一個變數時,可以使用:=,然而,若僅想更改已宣告變數的值,只需使用等於運算符(=)。

由於Go是一門類型化語言,我們無法將一種類型賦給另一種類型。例如,我們不能將值"Sammy"賦給一個int類型的變數:

i := 72
i = "Sammy"

嘗試相互賦予不同類型將導致編譯時錯誤:

Output
cannot use "Sammy" (type string) as type int in assignment

Go不允許我們多次使用同一個變數名:

var s string
var s string
Output
s redeclared in this block

如果我們試圖對同一變數名多次使用短變數宣告,同樣會收到編譯錯誤。這可能因疏忽而發生,因此理解錯誤訊息的含義對於排錯是有幫助的:

i := 5
i := 10
Output
no new variables on left side of :=

與變數宣告類似,對變數命名給予考慮將提高您及他人在未來回顧程序時的可讀性。

多重賦值

Go 也允許我們在同一行中將多個值賦予多個變數。這些值可以屬於不同的數據類型:

j, k, l := "shark", 2.05, 15
fmt.Println(j)
fmt.Println(k)
fmt.Println(l)
Output
shark 2.05 15

在這個例子中,變數 j 被賦予了字符串 "shark",變數 k 被賦予了浮點數 2.05,而變數 l 被賦予了整數 15

這種在一行中將多個變數賦予多個值的方法可以減少代碼的行數。然而,重要的是不要為了減少代碼行數而犧牲可讀性。

全局和局部變數

在使用變數的程序中,重要的是要考慮到變數的作用域。變數的作用域指的是在給定程序中可以訪問該變數的特定位置。這意味著並非所有變數都可以在給定程序的所有部分訪問——一些變數將是全局的,一些將是局部的。

全局變數存在於函數之外。局部變數存在於函數之內。

讓我們看看全局和局部變數的實際應用:

package main

import "fmt"


var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
}

func main() {
	printLocal()
	fmt.Println(g)
}
Output
local global

在這裡,我們使用 `var g = "global"` 在函數外部創建一個全局變量。然後我們定義函數 `printLocal()`。在函數內部,一個名為 `l` 的局部變量被賦值並打印出來。程序最後調用 `printLocal()` 並打印全局變量 `g`。

因為 `g` 是一個全局變量,我們可以在 `printLocal()` 中引用它。讓我們修改前面的程序來實現這一點:

package main

import "fmt"


var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
	fmt.Println(g)
}

func main() {
	printLocal()
	fmt.Println(g)
}
Output
local global global

我們首先聲明一個全局變量 `g`,即 `var g = "global"`。在 `main` 函數中,我們調用函數 `printLocal`,該函數聲明一個局部變量 `l` 並打印它,`fmt.Println(l)`。然後,`printLocal` 打印出全局變量 `g`,即 `fmt.Println(g)`。儘管 `g` 未在 `printLocal` 內定義,但它仍然可以被訪問,因為它是在全局範圍內聲明的。最後,`main` 函數也打印出 `g`。

現在讓我們嘗試在函數外部調用局部變量:

package main

import "fmt"

var g = "global"

func printLocal() {
	l := "local"
	fmt.Println(l)
}

func main() {
	fmt.Println(l)
}

Output
undefined: l

我們不能在賦值的函數外部使用局部變量。如果你嘗試這樣做,你會在編譯時收到一個 `undefined` 錯誤。

讓我們看另一個例子,其中我們對全局變量和局部變量使用相同的名稱:

package main

import "fmt"

var num1 = 5

func printNumbers() {
	num1 := 10
	num2 := 7  

	fmt.Println(num1)
	fmt.Println(num2)
}

func main() {
	printNumbers()
	fmt.Println(num1)
}
Output
10 7 5

在這個程式中,我們兩次宣告了 num1 變數。首先,我們在全域範疇宣告了 num1var num1 = 5,然後在 printNumbers 函式的區域範疇內再次宣告,num1 := 10。當我們從 main 程式中印出 num1 時,我們看到印出了值 5。這是因為 main 只看到全域變數的宣告。然而,當我們從 printNumbers 函式中印出 num1 時,它看到的是區域宣告,並會印出值 10。儘管 printNumbers 創建了一個名為 num1 的新變數並賦予其值 10,但它不會影響具有值 5 的全域 num1 實例。

在處理變數時,您還需要考慮程式的哪些部分需要存取每個變數;根據需要採用全域或區域變數。在 Go 程式中,您會發現區域變數通常更為常見。

常數

常數與變數相似,只是它們一旦宣告後就不能被修改。常數對於定義在程式中將多次使用但不可更改的值非常有用。

舉例來說,如果我們想要宣告一個購物車系統的稅率,我們可以使用常數,然後在程式的不同部分計算稅額。未來某個時刻,如果稅率變更,我們只需在程式中的一個地方修改該值。如果使用變數,有可能在程式的某處不小心改變了該值,這將導致不正確的計算結果。

宣告常數時,我們可以使用以下語法:

const shark = "Sammy"
fmt.Println(shark)
Output
Sammy

如果在宣告後嘗試修改常數,我們會遇到編譯時錯誤:

Output
cannot assign to shark

常數可以是 無類型 的。這在處理整數型別的數字時非常有用。如果常數是 無類型 的,它會被明確轉換,而 有類型 的常數則不會。來看看我們如何使用常數:

package main

import "fmt"

const (
	year     = 365
	leapYear = int32(366)
)

func main() {
	hours := 24
	minutes := int32(60)
	fmt.Println(hours * year)    
	fmt.Println(minutes * year)   
	fmt.Println(minutes * leapYear)
}
Output
8760 21900 21960

如果你宣告一個有類型的常數,它將是該特定類型。這裡當我們宣告常數 leapYear 時,我們將其定義為 int32 數據類型。因此它是一個 有類型 的常數,這意味著它只能與 int32 數據類型一起操作。我們宣告的 year 常數沒有指定類型,所以它被視為 無類型 的。正因如此,你可以將其用於任何整數數據類型。

hours 被定義時,它推斷出是 int 類型,因為我們沒有明確給它指定類型,hours := 24。當我們宣告 minutes 時,我們明確宣告它為 int32 類型,minutes := int32(60)

現在讓我們逐一走過每個計算,並解釋其為何有效:

hours * year

在這種情況下,hours 是一個 int,而 years無類型。當程式編譯時,它會明確地將 years 轉換為一個 int,這使得乘法操作能夠成功。

minutes * year

在這種情況下,minutes 是一個 int32,而 year無類型。當程式編譯時,它會明確地將 years 轉換為一個 int32,這使得乘法操作能夠成功。

minutes * leapYear

在這種情況下,minutes 是一個 int32,而 leapYear 是一個 有類型 的常數,類型為 int32。這次編譯器不需要做任何事情,因為兩個變數已經是相同的類型。

如果我們嘗試乘以兩個類型為 typed 且不相容的類型,程式將無法編譯:

fmt.Println(hours * leapYear)
Output
invalid operation: hours * leapYear (mismatched types int and int32)

在這種情況下,hours 被推斷為一個 int,而 leapYear 被明確宣告為一個 int32。因為 Go 是一種有類型的語言,一個 int 和一個 int32 在數學操作上不相容。要將它們相乘,你需要將其中一個轉換為 int32int

結論

在本教程中,我們回顧了 Go 語言中變數的一些常見使用場景。變數是編程的重要基石,作為代表程式中我們使用的資料類型值的符號。

Source:
https://www.digitalocean.com/community/tutorials/how-to-use-variables-and-constants-in-go