一文教你如何快速學(xué)會(huì)Go的struct數(shù)據(jù)類型
什么是結(jié)構(gòu)體
結(jié)構(gòu)是表示字段集合的用戶定義類型。它可以用于將數(shù)據(jù)分組為單個(gè)單元而不是將每個(gè)數(shù)據(jù)作為單獨(dú)的值的地方。
例如,員工有firstName、lastName和age。將這三個(gè)屬性分組到一個(gè)名為Employee。
type Employee struct {
firstName string
lastName string
age int
}上面的代碼段聲明了一個(gè)結(jié)構(gòu)類型Employee,其中包含字段firstName、lastName和age。上面的Employee結(jié)構(gòu)稱為命名結(jié)構(gòu),因?yàn)樗鼊?chuàng)建了一個(gè)名為Employme的新數(shù)據(jù)類型,可以使用該數(shù)據(jù)類型創(chuàng)建Employ結(jié)構(gòu)。
通過(guò)在一行中聲明屬于同一類型的字段,然后在類型名稱后面加上該字段,也可以使該結(jié)構(gòu)更加緊湊。在上面的struct中,firstName和lastName屬于同一類型字符串,因此該struct可以重寫(xiě)為:
type Employee struct {
firstName, lastName string
age int
}盡管上面的語(yǔ)法節(jié)省了幾行代碼,但它并沒(méi)有使字段聲明顯式。請(qǐng)避免使用上述語(yǔ)法。
創(chuàng)建結(jié)構(gòu)體
讓我們使用以下簡(jiǎn)單程序聲明一個(gè)命名的structEmployee。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
//creating struct specifying field names
emp1 := Employee{
firstName: "Sam",
age: 25,
salary: 500,
lastName: "Anderson",
}
//creating struct without specifying field names
emp2 := Employee{"Thomas", "Paul", 29, 800}
fmt.Println("Employee 1", emp1)
fmt.Println("Employee 2", emp2)
}在上述程序的第7行中,我們創(chuàng)建了一個(gè)命名的結(jié)構(gòu)類型Employee。在上述程序的第17行中,emp1結(jié)構(gòu)是通過(guò)為每個(gè)字段名指定值來(lái)定義的。聲明結(jié)構(gòu)類型時(shí),字段的順序不必與字段名的順序相同。在這種情況下。我們已更改lastName的位置并將其移到末尾。這將不會(huì)有任何問(wèn)題。
在上述程序的第25行中,通過(guò)省略字段名來(lái)定義emp2。在這種情況下,必須保持字段的順序與結(jié)構(gòu)聲明中指定的順序相同。請(qǐng)避免使用此語(yǔ)法,因?yàn)樗鼤?huì)使您難以確定哪個(gè)字段的值。我們?cè)诖颂幹付ù烁袷街皇菫榱死斫膺@也是一個(gè)有效語(yǔ)法:)
以上程序打印為:
Employee 1 {Sam Anderson 25 500}
Employee 2 {Thomas Paul 29 800}
創(chuàng)建匿名結(jié)構(gòu)體
可以在不創(chuàng)建新數(shù)據(jù)類型的情況下聲明結(jié)構(gòu)。這些類型的結(jié)構(gòu)稱為匿名結(jié)構(gòu)。
package main
import (
"fmt"
)
func main() {
emp3 := struct {
firstName string
lastName string
age int
salary int
}{
firstName: "Andreah",
lastName: "Nikola",
age: 31,
salary: 5000,
}
fmt.Println("Employee 3", emp3)
}在上述程序的第8行中,定義了一個(gè)匿名結(jié)構(gòu)變量emp3。正如我們已經(jīng)提到的,這個(gè)結(jié)構(gòu)稱為anonymous,因?yàn)樗粍?chuàng)建一個(gè)新的結(jié)構(gòu)變量emp3,而沒(méi)有定義任何新的結(jié)構(gòu)類型,如命名結(jié)構(gòu)。
上述代碼打印的結(jié)果為:
Employee 3 {Andreah Nikola 31 5000}
訪問(wèn)結(jié)構(gòu)體字段
運(yùn)算符.用于訪問(wèn)結(jié)構(gòu)的各個(gè)字段。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp6 := Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", emp6.firstName)
fmt.Println("Last Name:", emp6.lastName)
fmt.Println("Age:", emp6.age)
fmt.Printf("Salary: $%d\n", emp6.salary)
emp6.salary = 6500
fmt.Printf("New Salary: $%d", emp6.salary)
}上面程序中的emp6.firstName訪問(wèn)emp6結(jié)構(gòu)的firstName字段。在第25行中,我們修改了員工的工資。此程序打印。
First Name: Sam
Last Name: Anderson
Age: 55
Salary: $6000
New Salary: $6500
結(jié)構(gòu)體零值
當(dāng)定義了一個(gè)結(jié)構(gòu)并且沒(méi)有用任何值顯式初始化它時(shí),默認(rèn)情況下會(huì)為該結(jié)構(gòu)的字段分配零值。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
var emp4 Employee //zero valued struct
fmt.Println("First Name:", emp4.firstName)
fmt.Println("Last Name:", emp4.lastName)
fmt.Println("Age:", emp4.age)
fmt.Println("Salary:", emp4.salary)
}上面的程序定義了emp4,但沒(méi)有用任何值初始化。因此,firstName和lastName被指定為字符串的零值,字符串為空字符串“”,age和salary被指定為零值int,即0。此程序打印
First Name:
Last Name:
Age: 0
Salary: 0
也可以為某些字段指定值,而忽略其余字段。在這種情況下,被忽略的字段被賦值為零。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp5 := Employee{
firstName: "John",
lastName: "Paul",
}
fmt.Println("First Name:", emp5.firstName)
fmt.Println("Last Name:", emp5.lastName)
fmt.Println("Age:", emp5.age)
fmt.Println("Salary:", emp5.salary)
}在上面的程序中。第16號(hào)和第17號(hào),firstName和lastName被初始化,而年齡和薪水沒(méi)有初始化。因此,年齡和工資被指定為零值。此程序輸出:
First Name: John
Last Name: Paul
Age: 0
Salary: 0
結(jié)構(gòu)體指針
也可以創(chuàng)建指向結(jié)構(gòu)的指針。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp8 := &Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", (*emp8).firstName)
fmt.Println("Age:", (*emp8).age)
}上面程序中的emp8是指向Employee結(jié)構(gòu)的指針。(*emp8)。firstName是訪問(wèn)emp8結(jié)構(gòu)的firstName字段的語(yǔ)法。此程序打印:
First Name: Sam
Age: 55
Go語(yǔ)言為我們提供了使用emp8.firstName而不是顯式取消引用(*emp8)的選項(xiàng)。firstName以訪問(wèn)firstName字段。
package main
import (
"fmt"
)
type Employee struct {
firstName string
lastName string
age int
salary int
}
func main() {
emp8 := &Employee{
firstName: "Sam",
lastName: "Anderson",
age: 55,
salary: 6000,
}
fmt.Println("First Name:", emp8.firstName)
fmt.Println("Age:", emp8.age)
}我們已經(jīng)使用emp8.firstName訪問(wèn)上述程序中的firstName字段,該程序還輸出:
First Name: Sam
Age: 55
匿名字段
可以使用只包含類型而不包含字段名的字段創(chuàng)建結(jié)構(gòu)。這類字段稱為匿名字段。下面的代碼段創(chuàng)建了一個(gè)struct Person,它有兩個(gè)匿名字段string和int:
type Person struct {
string
int
}即使匿名字段沒(méi)有顯式名稱,默認(rèn)情況下,匿名字段的名稱是其類型的名稱。例如,在上面的Person結(jié)構(gòu)中,雖然字段是匿名的,但默認(rèn)情況下它們采用字段類型的名稱。所以Person結(jié)構(gòu)有兩個(gè)字段,分別是名稱字符串和int。
package main
import (
"fmt"
)
type Person struct {
string
int
}
func main() {
p1 := Person{
string: "naveen",
int: 50,
}
fmt.Println(p1.string)
fmt.Println(p1.int)
}在上述程序的第17行和第18行中,我們?cè)L問(wèn)Person結(jié)構(gòu)的匿名字段,使用它們的類型作為字段名,分別是string和int。上述程序的輸出為:
naveen
50
結(jié)構(gòu)體嵌套
結(jié)構(gòu)可能包含字段,而字段又是結(jié)構(gòu)。這些類型的結(jié)構(gòu)稱為嵌套結(jié)構(gòu)。
package main
import (
"fmt"
)
type Address struct {
city string
state string
}
type Person struct {
name string
age int
address Address
}
func main() {
p := Person{
name: "Naveen",
age: 50,
address: Address{
city: "Chicago",
state: "Illinois",
},
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.address.city)
fmt.Println("State:", p.address.state)
}上述程序中的Person結(jié)構(gòu)具有字段地址,而字段地址又是一個(gè)結(jié)構(gòu)。此程序打印:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
字段升級(jí)
屬于結(jié)構(gòu)中匿名結(jié)構(gòu)字段的字段稱為提升字段,因?yàn)榭梢韵裨L問(wèn)包含匿名結(jié)構(gòu)字段結(jié)構(gòu)一樣訪問(wèn)它們。我可以理解這個(gè)定義相當(dāng)復(fù)雜,所以讓我們深入研究一些代碼來(lái)理解它。
type Address struct {
city string
state string
}
type Person struct {
name string
age int
Address
}在上面的代碼段中,Person結(jié)構(gòu)有一個(gè)匿名字段Address,它是一個(gè)結(jié)構(gòu)。現(xiàn)在,Address的字段,即city和state,被稱為promoted字段,因?yàn)榭梢韵裰苯釉赑erson結(jié)構(gòu)本身中聲明一樣訪問(wèn)它們。
package main
import (
"fmt"
)
type Address struct {
city string
state string
}
type Person struct {
name string
age int
Address
}
func main() {
p := Person{
name: "Naveen",
age: 50,
Address: Address{
city: "Chicago",
state: "Illinois",
},
}
fmt.Println("Name:", p.name)
fmt.Println("Age:", p.age)
fmt.Println("City:", p.city) //city is promoted field
fmt.Println("State:", p.state) //state is promoted field
}在上面程序的第29行和第30行中,可以訪問(wèn)提升字段city和state,就好像它們是使用語(yǔ)法p.city和p.state在結(jié)構(gòu)p中聲明的一樣。此程序打印:
Name: Naveen
Age: 50
City: Chicago
State: Illinois
結(jié)構(gòu)體導(dǎo)出
如果結(jié)構(gòu)類型以大寫(xiě)字母開(kāi)頭,則它是導(dǎo)出類型,可以從其他包訪問(wèn)。類似地,如果結(jié)構(gòu)的字段以caps開(kāi)頭,則可以從其他包訪問(wèn)它們。讓我們編寫(xiě)一個(gè)具有自定義包的程序來(lái)更好地理解這一點(diǎn)。在Documents目錄中創(chuàng)建名為structs的文件夾。請(qǐng)隨意在任何您喜歡的地方創(chuàng)建它。我更喜歡我的文檔目錄。
mkdir ~/Documents/structs
創(chuàng)建一個(gè)gomod,并命名為structs。
cd ~/Documents/structs/
go mod init structs
創(chuàng)建另外一個(gè)目錄computer申明一個(gè)結(jié)構(gòu)體。
mkdir computer
創(chuàng)建一個(gè)spec.go文件,并寫(xiě)入如下內(nèi)容:
package computer
type Spec struct { //exported struct
Maker string //exported field
Price int //exported field
model string //unexported field
}上面的代碼片段創(chuàng)建了一個(gè)程序包計(jì)算機(jī),其中包含一個(gè)導(dǎo)出的結(jié)構(gòu)類型Spec,其中有兩個(gè)導(dǎo)出的字段Maker和Price,以及一個(gè)未導(dǎo)出的字段模型。讓我們從主包導(dǎo)入這個(gè)包并使用Spec結(jié)構(gòu)。
創(chuàng)建名為main的文件。進(jìn)入structs目錄并在main.go中編寫(xiě)以下程序:
package main
import (
"structs/computer"
"fmt"
)
func main() {
spec := computer.Spec {
Maker: "apple",
Price: 50000,
}
fmt.Println("Maker:", spec.Maker)
fmt.Println("Price:", spec.Price)
}這個(gè)結(jié)構(gòu)體如下結(jié)構(gòu)體:
├── structs
│ ├── computer
│ │ └── spec.go
│ ├── go.mod
│ └── main.go
在上面程序的第4行,我們導(dǎo)入計(jì)算機(jī)包。在第13行和第14行,我們?cè)L問(wèn)struct Spec的兩個(gè)導(dǎo)出字段Maker和Price。這個(gè)程序可以通過(guò)執(zhí)行命令go-install,然后執(zhí)行structs命令來(lái)運(yùn)行。
go install
structs
運(yùn)行之后如下結(jié)果:
Maker: apple
Price: 50000
如果我們?cè)噲D訪問(wèn)未報(bào)告的字段模型,編譯器會(huì)抱怨。更換main的內(nèi)容。使用以下代碼。
package main
import (
"structs/computer"
"fmt"
)
func main() {
spec := computer.Spec {
Maker: "apple",
Price: 50000,
model: "Mac Mini",
}
fmt.Println("Maker:", spec.Maker)
fmt.Println("Price:", spec.Price)
}在上述程序的第12行中,我們嘗試訪問(wèn)未報(bào)告的字段模型。運(yùn)行此程序?qū)?dǎo)致編譯錯(cuò)誤。
# structs
./main.go:12:13: unknown field 'model' in struct literal of type computer.Spec
由于模型字段未報(bào)告,因此無(wú)法從其他包訪問(wèn)它。
結(jié)構(gòu)體比較
結(jié)構(gòu)是值類型,如果它們的每個(gè)字段都是可比較的,則可以進(jìn)行比較。如果兩個(gè)結(jié)構(gòu)變量的對(duì)應(yīng)字段相等,則認(rèn)為它們相等。
package main
import (
"fmt"
)
type name struct {
firstName string
lastName string
}
func main() {
name1 := name{
firstName: "Steve",
lastName: "Jobs",
}
name2 := name{
firstName: "Steve",
lastName: "Jobs",
}
if name1 == name2 {
fmt.Println("name1 and name2 are equal")
} else {
fmt.Println("name1 and name2 are not equal")
}
name3 := name{
firstName: "Steve",
lastName: "Jobs",
}
name4 := name{
firstName: "Steve",
}
if name3 == name4 {
fmt.Println("name3 and name4 are equal")
} else {
fmt.Println("name3 and name4 are not equal")
}
}在上面的程序中,名稱結(jié)構(gòu)類型包含兩個(gè)字符串字段。由于字符串是可比較的,因此可以比較類型名的兩個(gè)結(jié)構(gòu)變量。在上面的程序中,name1和name2相等,而name3和name4不相等。此程序?qū)⑤敵?
name1 and name2 are equal
name3 and name4 are not equal
如果結(jié)構(gòu)變量包含不可比較的字段,那么它們就不可比較(感謝reddit的alasija指出這一點(diǎn))。
package main
import (
"fmt"
)
type image struct {
data map[int]int
}
func main() {
image1 := image{
data: map[int]int{
0: 155,
}}
image2 := image{
data: map[int]int{
0: 155,
}}
if image1 == image2 {
fmt.Println("image1 and image2 are equal")
}
}在上面的程序中,圖像結(jié)構(gòu)類型包含類型映射的字段數(shù)據(jù)。地圖是不可比較的,因此無(wú)法比較image1和image2。如果運(yùn)行此程序,編譯將失敗并返回錯(cuò)誤。
./prog.go:20:12: invalid operation: image1 == image2 (struct containing map[int]int cannot be compared)
到此這篇關(guān)于一文教你如何快速學(xué)會(huì)Go的struct數(shù)據(jù)類型的文章就介紹到這了,更多相關(guān)Go struct數(shù)據(jù)類型內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GoFrame框架gcache的緩存控制淘汰策略實(shí)踐示例
這篇文章主要為大家介紹了GoFrame框架gcache的緩存控制淘汰策略的實(shí)踐示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Go 互斥鎖和讀寫(xiě)互斥鎖的實(shí)現(xiàn)
本文主要介紹了Go 互斥鎖和讀寫(xiě)互斥鎖的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
Go關(guān)鍵字defer的使用和底層實(shí)現(xiàn)
defer是Go語(yǔ)言的關(guān)鍵字,一般用于資源的釋放和異常的捕捉,defer語(yǔ)句后將其后面跟隨的語(yǔ)句進(jìn)行延遲處理,就是說(shuō)在函數(shù)執(zhí)行完畢后再執(zhí)行調(diào)用,也就是return的ret指令之前,本文給大家介紹了Go關(guān)鍵字defer的使用和底層實(shí)現(xiàn),需要的朋友可以參考下2023-11-11
golang利用不到20行代碼實(shí)現(xiàn)路由調(diào)度詳解
這篇文章主要給大家介紹了關(guān)于golang利用不到20行代碼實(shí)現(xiàn)路由調(diào)度的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
golang如何通過(guò)type定義函數(shù)類型
這篇文章主要介紹了golang如何通過(guò)type定義函數(shù)類型問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01

