
文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载本篇為《Build Web Application with Golang》第 2.4 節的深度精讀主題為 Go 語言的struct型別如何宣告一個承載多個欄位的自訂型別、四種初始化與使用方式、struct 作為函式參數的傳值行為以及最具 Go 特色的**匿名字段嵌入欄位**機制——包括 struct、自訂型別、內建型別嵌入與欄位名稱衝突時的最外層優先規則。讀完本篇你將能在 Web 開發中熟練地用 struct 組織資料模型例如使用者、員工等實體並理解後續章節物件導向、介面、模板渲染等的型別基礎。structGo 語言中的型別容器在 Go 語言中與 C 或其他語言一樣我們可以宣告新的型別作為其它型別的屬性或欄位的容器。例如我們可以建立一個自訂型別person代表一個人的實體——這個實體擁有兩個屬性姓名與年齡。這種型別在 Go 中稱之為structtype person struct { name string age int }宣告一個 struct 就是如此簡單type關鍵字宣告新型別struct關鍵字定義欄位集合。上述型別包含兩個欄位一個string型別的欄位name用來儲存使用者名稱這個屬性一個int型別的欄位age用來儲存使用者年齡這個屬性。從型別設計的角度看struct 是把多個相關資料聚合成一個單一實體entity的基礎工具。在 Web 應用中表單輸入、資料庫記錄、JSON 回應等幾乎都可以對應到一個 struct——本書後續章節的資料模型如使用者、文章、留言大多以此為基礎展開。如何使用 struct宣告、賦值與存取宣告 struct 之後最直接的使用方式是先宣告一個變數再逐欄位賦值與讀取type person struct { name string age int } var P person // P 現在就是 person 型別的變數了 P.name Astaxie // 賦值Astaxie給 P 的 name 屬性 P.age 25 // 賦值25給變數 P 的 age 屬性 fmt.Printf(The persons name is %s, P.name) // 存取 P 的 name 屬性這裡的關鍵點是欄位透過點號.存取P.name與P.age的語法與 C 語言一致對剛接觸 Go 的開發者非常直觀。除了「先宣告、後賦值」之外Go 還提供了另外幾種宣告與初始化方式按照順序提供初始化值位置參數欄位順序必須與定義完全一致P : person{Tom, 25}透過field:value的方式初始化這種寫法可以任意順序、不必全部填寫P : person{age:24, name:Tom}透過new函式分配一個指標此時P的型別為*personP : new(person)實務建議位置初始化person{Tom, 25}寫起來簡短但一旦欄位調整順序就容易被誤植而field:value方式person{age:24, name:Tom}可讀性更高、對欄位重組更友善是 Web 專案中更推薦的寫法。new(person)會回傳指標並將所有欄位初始化為零值name為空字串、age為 0適合稍後動態賦值的場景。完整範例struct 作為函式參數傳值語義下面是一個完整可執行的 struct 使用範例——比較兩個人的年齡回傳年齡較大者與年齡差。這個範例同時驗證了 Go 的一個重要特性struct 是傳值的passed by value函式內部對參數的修改不會影響呼叫端的原始變數package main import fmt // 宣告一個新的型別 type person struct { name string age int } // 比較兩個人的年齡回傳年齡大的那個人並且回傳年齡差 // struct 也是傳值的 func Older(p1, p2 person) (person, int) { if p1.agep2.age { // 比較 p1 和 p2 這兩個人的年齡 return p1, p1.age-p2.age } return p2, p2.age-p1.age } func main() { var tom person // 賦值初始化 tom.name, tom.age Tom, 18 // 兩個欄位都寫清楚的初始化 bob : person{age:25, name:Bob} // 按照 struct 定義順序初始化值 paul : person{Paul, 43} tb_Older, tb_diff : Older(tom, bob) tp_Older, tp_diff : Older(tom, paul) bp_Older, bp_diff : Older(bob, paul) fmt.Printf(Of %s and %s, %s is older by %d years\n, tom.name, bob.name, tb_Older.name, tb_diff) fmt.Printf(Of %s and %s, %s is older by %d years\n, tom.name, paul.name, tp_Older.name, tp_diff) fmt.Printf(Of %s and %s, %s is older by %d years\n, bob.name, paul.name, bp_Older.name, bp_diff) }程式輸出三組比較結果Tom vs Bob、Tom vs Paul、Bob vs Paul並分別印出年長者姓名與年齡差。Older函式一次回傳兩個值年長者的 person 與年齡差這也展示了 Go 函式多重回傳值的慣用寫法。傳值補充由於 struct 是傳值的函式每次呼叫都會複製一份完整結構。對大型 struct含大量欄位或內嵌 slice/map 之外的資料而言實務上常改用指標參數如func Older(p1, p2 *person)來避免複製開銷但若希望保持不可變語義則維持傳值即可。匿名字段嵌入欄位欄位的繼承與組合上述範例中struct 定義時欄位名與其型別一一對應。實際上Go 支援只提供型別、而不寫欄位名的宣告方式這就是匿名字段anonymous field也稱為嵌入欄位embedded field。當匿名字段是一個 struct 時這個 struct 所擁有的全部欄位都會被隱含地引入當前定義的 struct——相當於實現了「欄位的繼承」與「型別組合」。來看一個具體範例package main import fmt type Human struct { name string age int weight int } type Student struct { Human // 匿名字段那麼預設 Student 就包含了 Human 的所有欄位 speciality string } func main() { // 我們初始化一個學生 mark : Student{Human{Mark, 25, 120}, Computer Science} // 我們存取相應的欄位 fmt.Println(His name is , mark.name) fmt.Println(His age is , mark.age) fmt.Println(His weight is , mark.weight) fmt.Println(His speciality is , mark.speciality) // 修改對應的備註資訊 mark.speciality AI fmt.Println(Mark changed his speciality) fmt.Println(His speciality is , mark.speciality) // 修改他的年齡資訊 fmt.Println(Mark become old) mark.age 46 fmt.Println(His age is, mark.age) // 修改他的體重資訊 fmt.Println(Mark is not an athlet anymore) mark.weight 60 fmt.Println(His weight is, mark.weight) }Student 組合了 Human struct 與 string 基本型別結構關係如下圖圖 2.7Student 透過匿名字段 Human 繼承了 name、age、weight 三個欄位同時保有自身的 speciality 欄位。從輸出可以看到存取mark.name、mark.age、mark.weight時就像存取 Student 自己擁有的欄位一樣——匿名字段實現了欄位的繼承。更進一步Student 還能以Human作為欄位名直接存取整個嵌入的 structmark.Human Human{Marcus, 55, 220} mark.Human.age - 1這意味著我們既可以「扁平化」地直接存取繼承而來的欄位mark.age也可以「分層」地存取整個嵌入物件mark.Human兩種存取方式並存。所有型別皆可嵌入自訂型別與內建型別匿名字段不限於 struct——所有的內建型別和自訂型別都可以作為匿名字段。下面的範例展示了三種嵌入structHuman、自訂 slice 型別Skills、以及內建型別intpackage main import fmt type Skills []string type Human struct { name string age int weight int } type Student struct { Human // 匿名字段struct Skills // 匿名字段自訂的型別 string slice int // 內建型別作為匿名字段 speciality string } func main() { // 初始化學生 Jane jane : Student{Human:Human{Jane, 35, 100}, speciality:Biology} // 現在我們來存取相應的欄位 fmt.Println(Her name is , jane.name) fmt.Println(Her age is , jane.age) fmt.Println(Her weight is , jane.weight) fmt.Println(Her speciality is , jane.speciality) // 我們來修改他的 skill 技能欄位 jane.Skills []string{anatomy} fmt.Println(Her skills are , jane.Skills) fmt.Println(She acquired two new ones ) jane.Skills append(jane.Skills, physics, golang) fmt.Println(Her skills now are , jane.Skills) // 修改匿名內建型別欄位 jane.int 3 fmt.Println(Her preferred number is, jane.int) }從這個範例可以得到兩個結論struct 不僅能將 struct 作為匿名字段自訂型別與內建型別同樣可以欄位名即型別名本身例如int欄位透過jane.int存取。可以在嵌入欄位上直接進行函式操作——例如append(jane.Skills, physics, golang)對嵌入的Skills本質是[]string執行 slice 追加這說明了嵌入欄位保留了其型別的完整操作能力。注意Student{Human:Human{Jane, 35, 100}, speciality:Biology}這種初始化方式是對具名嵌入欄位使用field:value語法若省略欄位名如Student{Human{...}, Biology}則必須依照欄位宣告順序。欄位名稱衝突最外層優先與過載現在考慮一個問題如果Human裡面有一個欄位叫做phone而Student這裡以Employee為例也有一個欄位叫做phone該怎麼辦Go 很簡單地解決了這個問題——最外層的優先存取。當你透過student.phone或employee.phone存取時實際存取的是當前 struct 自己宣告的phone欄位而不是嵌入的Human裡的phone欄位package main import fmt type Human struct { name string age int phone string // Human 型別擁有的欄位 } type Employee struct { Human // 匿名字段 Human speciality string phone string // 僱員的 phone 欄位 } func main() { Bob : Employee{Human{Bob, 34, 777-444-XXXX}, Designer, 333-222} fmt.Println(Bobs work phone is:, Bob.phone) // 如果我們要存取 Human 的 phone 欄位 fmt.Println(Bobs personal phone is:, Bob.Human.phone) }程式輸出Bobs work phone is: 333-222 Bobs personal phone is: 777-444-XXXX這個機制允許我們過載覆寫透過匿名字段繼承的欄位Employee用自己的phone遮蔽了Human.phone。如果仍需要存取被遮蔽的嵌入欄位就透過匿名欄位名顯式定址——Bob.Human.phone取回的是 Human 的私人電話。遮蔽深度提醒Go 的欄位解析依循「外層優先」規則逐層向外搜尋若在相同深度存在同名欄位例如兩個嵌入 struct 同時提供同名欄位Go 會在編譯期判定為歧義錯誤ambiguous selector需要顯式指定完整路徑來消除歧義。在倉庫中驗證與執行本主題對應的可執行範例已收錄在本倉庫的 en/code/src/apps/ch.2.4 目錄讀者可以直接在本地執行驗證compare_age/main.go完整的 person struct 與Older比較函式範例註解明確標示「struct is passed by value」embedded_structs/main.goStudent 嵌入 Human struct 的欄位繼承範例embedded_structs2/main.goSkills自訂 slice與 int內建型別作為匿名字段的進階範例embedded_structs_with_name_conflict/main.gophone 欄位名稱衝突、最外層優先與Bob.Human.phone顯式存取的範例main.go彙整了四種建立 struct 的方式並示範%v與%#v格式化輸出 struct 的除錯技巧。執行方式很簡單進入對應子目錄後以go run main.go執行即可看到本文所述的全部輸出。中文版對應文件為 zh/02.4.md「struct 类型」英文版為 en/02.4.mdChapter 2.4。小結與後續銜接本節掌握的要點可濃縮為宣告type person struct { name string; age int }建立型別容器初始化位置初始化、field:value初始化、new指標分配三種方式並存傳值語義struct 作為函式參數時是複製傳遞可依場景改用指標匿名字段只寫型別不寫欄位名支援 struct、自訂型別與內建型別的嵌入實現欄位繼承與組合衝突規則最外層優先透過匿名字段名顯式存取被遮蔽的欄位。這些能力是 Go 物件導向2.5 節物件導向的基石——下一節將在 struct 之上附加方法method與接收者receiver進而形成類別式的設計模式。若想回顧基礎語法可先複習流程與函式或返回目錄瀏覽全書章節。赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐Go 语言 struct 完全指南定义、初始化、嵌入字段与名称遮蔽build-web-application-with-golang 第 2.4 节Go 语言 struct 完全指南定义、初始化、嵌入字段与名称遮蔽build web application with golang 第 2.4 节 本篇文档教程Build Web Application with Golang 第 2.4 章Go struct 结构体完全指南定义、初始化与内嵌字段Build Web Application with Golang 第 2.4 章Go struct 结构体完全指南定义、初始化与内嵌字段 本篇指南以《B文档教程Go 语言 struct 完全指南定义、初始化与嵌入字段实战Build Web Application with Golang 第 2.4 节Go 语言 struct 完全指南定义、初始化与嵌入字段实战Build Web Application with Golang 第 2.4 节 struc文档教程上一篇OpenCore Legacy Patcher老 Mac 免费装新 macOS 的 3 步教程下一篇魔兽争霸III还在卡顿变形3分钟用WarcraftHelper解锁高帧率与宽屏创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考