در زبان گو ساختار به کالکشنی از فیلدها با تایپ های مختلف می گویند. شما با استفاده ساختار می توانید ساختار یا مدل کلی از بدنه پروژه خود را بنویسید. برای نمونه ما یک ساختار employee مثال زدیم تا با ساختار آشنا شوید.
نکته: ساختار میتواند بصورت خالی نیز جهت برخی اهداف ایجاد گردد.
1type sample struct {}
برای ایجاد ساختار باید از کلمه کلیدی
type
استفاده کنید.داخل بدنه ساختار فیلدها را تعریف کنید.
- فیلد name از نوع string
- فیلد age از نوع int
- فیلد salary از نوع int
ساختار زبان گو را با class در سایر زبان ها مقایسه می کنند.
2.2.1 تعریف تایپ struct #
در زیر یک مثال ساده برای اینکه بتوانید در هر جای بدنه فایل go
ساختار
تعریف کنید زدیم :
در بالا ما ۲ تا فیلد برای ساختار
تعریف کردیم که هر دو فیلد از نوع float64
می باشند.
2.2.2 ایجاد یک متغیر ساختار (struct) #
برای ایجاد یک متغیر ساختار می توانید خیلی ساده تعریف کنید و خانه ای از حافظه را برای متغیر در نظر گرفته شود.
1emp := employee{}
در بالا یک متغیر با مقدار پیش فرض صفر ساختار employee تعریف کردیم.
زمانیکه یک متغیر ساختار تعریف می کنید مقدار استفاده از حافظه 0 بایت می باشد.
- ایجاد متغیر ساختار و مقدار دهی فیلدها در یک خط :
1emp := employee{name: "Sam", age: 31, salary: 2000}
- ایجاد متغیر ساختار و مقدار دهی فیلد در خط های مختلف (توصیه می شود) :
توجه کنید هیچ اجباری نیست حتما فیلدی را مقدار دهی کنید چون می توانید در جاهای دیگر یا هر زمانیکه نیاز داشتید مقدار دهی کنید.
در بالا ما فیلد salary را مقدار دهی نکردیم و بطور پیش فرض کامپایلر با توجه به تایپ فیلد مقدار پیش فرض صفر اون تایپ را در نظر میگیرد.
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9}
10
11func main() {
12 emp1 := employee{}
13 fmt.Printf("Emp1: %+v\n", emp1)
14
15 emp2 := employee{name: "Sam", age: 31, salary: 2000}
16 fmt.Printf("Emp2: %+v\n", emp2)
17
18 emp3 := employee{
19 name: "Sam",
20 age: 31,
21 salary: 2000,
22 }
23 fmt.Printf("Emp3: %+v\n", emp3)
24
25 emp4 := employee{
26 name: "Sam",
27 age: 31,
28 }
29 fmt.Printf("Emp4: %+v\n", emp4)
30}
1$ go run main.go
2Emp1: {name: age:0 salary:0}
3Emp2: {name:Sam age:31 salary:2000}
4Emp3: {name:Sam age:31 salary:2000}
5Emp4: {name:Sam age:31 salary:0}
- ایجاد متغیر ساختار و مقدار دهی فیلدها بدون نام فیلد :
شما می توانید فیلدها را بدون اینکه نام فیلد را قرار دهید مقدار دهی کنید اما از نظر تکنیکی اینکار توصیه نمی شود.
1emp := employee{"Sam", 31, 2000}
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9}
10
11func main() {
12 emp := employee{"Sam", 31, 2000}
13 fmt.Printf("Emp: %+v\n", emp)
14}
2.2.3 دسترسی و تنظیم فیلدهای ساختار (struct) #
زمانیکه شما یک متغیر ساختار تعریف می کنید می توانید خیلی آسان با استفاده از همان متغیر به فیلدهای ساختار دسترسی پیدا کنید و مقدار هر کدام از فیلدها را تغییر دهید.
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9}
10
11func main() {
12 emp := employee{name: "Sam", age: 31, salary: 2000}
13
14 //Accessing a struct field
15 n := emp.name
16 fmt.Printf("Current name is: %s\n", n)
17
18 //Assigning a new value
19 emp.name = "John"
20 fmt.Printf("New name is: %s\n", emp.name)
21}
2.2.4 کار با اشاره گر (Pointer) در ساختار (struct) #
شما برای ایجاد یک struct از نوع اشاره گر از دو حالت زیر می توانید استفاده کنید :
- با استفاده از عملگر
&
که اشاره به خانه حافظه دارد - با استفاده از تابع
new
2.2.4.1 ایجاد ساختار با استفاده عملگر & #
برای اینکه بتوانید یک ساختار از نوع اشاره گر
ایجاد کنید می توانید از عملگر &
استفاده کنید.
حتی یک ساختار اشاره گر می تواند مستقیما ایجاد شود :
1empP := &employee{name: "Sam", age: 31, salary: 2000}
به کد زیر توجه کنید :
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9}
10
11func main() {
12 emp := employee{name: "Sam", age: 31, salary: 2000}
13 empP := &emp
14 fmt.Printf("Emp: %+v\n", empP)
15 empP = &employee{name: "John", age: 30, salary: 3000}
16 fmt.Printf("Emp: %+v\n", empP)
17}
2.2.4.2 ایجاد ساختار با استفاده تابع new #
1func new(Type) *Type
با استفاده از تابع new
:
- شما یک ساختار ایجاد می کنید.
- سپس فیلدها با مقدار پیش فرض صفر مقدار دهی اولیه می شوند.
- در نهایت ساختار شما از نوع اشاره گر بازگشت داده می شود.
1empP := new(employee)
برای اینکه آدرس خانه حافظه ساختار از نوع اشاره گر را ببینید کافیه :
1fmt.Printf("Emp Pointer: %p\n", empP)
برای اینکه مقدار هر یک از فیلدها را ببینید کافیه :
1fmt.Printf("Emp Value: %+v\n", *empP)
در زیر یک کد نمونه قرار دادیم به عنوان مثال :
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9}
10
11func main() {
12 empP := new(employee)
13 fmt.Printf("Emp Pointer Address: %p\n", empP)
14 fmt.Printf("Emp Pointer: %+v\n", empP)
15 fmt.Printf("Emp Value: %+v\n", *empP)
16}
1$ go run main.go
2Emp Pointer Address: 0xc000130000
3Emp Pointer: &{name: age:0 salary:0}
4Emp Value: {name: age:0 salary:0}
2.2.5 چاپ یک متغیر ساختار (struct) #
برای اینکه بتوانید یک متغیر را چاپ کنید از دو روش زیر می توانید استفاده کنید و توجه کنید متغیر ساختار بصورت key/value هستش.
- با استفاده از پکیج fmt
- چاپ متغیر ساختار با استفاده از پکیج json/encoding
2.2.5.1 چاپ با استفاده از fmt #
در پکیج fmt ۲ تا تابع کاربردی جهت چاپ وجود دارد که اکثر اوقات استفاده می کنیم :
- تابع
Println
ورودی را با فرمت پیش فرض چاپ و در نهایت خط جدید در نظر میگیرد. - تابع
Printf
ورودی را با فرمت مشخص شده چاپ می کند و دست شما برای تعیین فرمت باز می باشد.
در زیر ما یک نمونه از employee ایجاد کردیم :
1emp := employee{name: "Sam", age: 31, salary: 2000}
حال به شیوه های زیر با استفاده از تابع Printf
ساختار را چاپ کردیم :
1fmt.Printf("%v", emp) - {Sam 31 2000}
1fmt.Printf("%+v", emp) - {name:Sam age:31 salary:2000}
- %v - مقدار هر کدام از فیلدهای ساختار چاپ می کند.
- %+v - مقدار هرکدام از فیلدها به همراه اسم فیلد چاپ می کند.
در زیر با استفاده از از تابع Println
ساختار را چاپ کردیم :
1fmt.Println(emp) - {Sam 31 2000}
در نهایت کد زیر یک مثال کلی از چاپ با استفاده از پکیج fmt می باشد :
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9}
10
11func main() {
12 emp := employee{name: "Sam", age: 31, salary: 2000}
13 fmt.Printf("Emp: %v\n", emp)
14 fmt.Printf("Emp: %+v\n", emp)
15 fmt.Printf("Emp: %#v\n", emp)
16 fmt.Println(emp)
17}
1$ go run main.go
2Emp: {Sam 31 2000}
3Emp: {name:Sam age:31 salary:2000}
4Emp: main.employee{name:"Sam", age:31, salary:2000}
5{Sam 31 2000}
2.2.5.2 چاپ ساختار با استفاده از پکیج JSON #
در این روش ما با استفاده از ۲ تابع Marshal و MarshalIndent پکیج json ساختار را encode می کنیم. و در نهایت خروجی encode شده را چاپ می کنیم.
- Marshal - در این تابع به عنوان ورودی ساختار را پاس می دهیم و در نهایت ۲ خروجی از نوع بایت و خطا دریافت می کنیم.
1Marshal(v interface{}) ([]byte, error)
- MarhsalIndent - با استفاده از این تابع ۳ تا ورودی پاس می دهیم به ترتیب ساختار, پیشوند و indent و در نهایت ۲ خروجی از نوع بایت و خطا دریافت می کنیم.
1MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
حالا با استفاده از توابع فوق یک کد نمونه مثال می زنیم چطور از این توابع استفاده کنید :
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7)
8
9type employee struct {
10 Name string
11 Age int
12 salary int
13}
14
15func main() {
16 emp := employee{Name: "Sam", Age: 31, salary: 2000}
17 //Marshal
18 empJSON, err := json.Marshal(emp)
19 if err != nil {
20 log.Fatalf(err.Error())
21 }
22 fmt.Printf("Marshal funnction output %s\n", string(empJSON))
23
24 //MarshalIndent
25 empJSON, err = json.MarshalIndent(emp, "", " ")
26 if err != nil {
27 log.Fatalf(err.Error())
28 }
29 fmt.Printf("MarshalIndent funnction output %s\n", string(empJSON))
30}
1$ go run main.go
2Marshal funnction output {"Name":"Sam","Age":31}
3
4MarshalIndent funnction output {
5 "Name": "Sam",
6 "Age": 31
7}
برای اطلاعات بیشتر در خصوص پکیج json می توانید به بخش آموزش کار با jsonمراجعه کنید.
2.2.6 کار با تگ ها در ساختار (struct) #
در ساختار زبان گو شما امکان اضافه کردن metadata به هر یک از فیلدها را دارید که ما به عنوان تگ می شناسیم که تگ ها برای انجام یکسری عملیات خاص نظیر encode/decode, اعتبارسنجی مقادیر فیلدها و … کمک می کند و یکی از کاربردی ترین عناوین در ساختار می باشد.
در زیر ما یک نمونه ساختار به همراه تگ تعریف کردیم :
تگ json به شما برای تعیین نام key داخل json کمک می کند و شما می توانید عنوان هر یک از فیلدهای بدنه json را یک نام اختصاصی قرار دهید و با استفاده از اون نام اختصاصی عملیات encode/decode در json انجام دهید.
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "log"
7)
8
9type employee struct {
10 Name string `json:"n"`
11 Age int `json:"a"`
12 Salary int `json:"s"`
13}
14
15func main() {
16 emp := employee{Name: "Sam", Age: 31, Salary: 2000}
17 //Converting to jsonn
18 empJSON, err := json.MarshalIndent(emp, '', ' ')
19 if err != nil {
20 log.Fatalf(err.Error())
21 }
22 fmt.Println(string(empJSON))
23}
در خروجی بالا نام key هر کدام از فیلدها تغییر کرده و دقت کنید ارتباط فیلدهای ساختار شما با فیلدهای json به واسطه تگی که تعیین کردید می باشد.
2.2.7 تعریف فیلد ناشناس در ساختار (struct) #
در ساختار امکان تعریف فیلدهای ناشناس را دارید و می توانید فیلد ناشناس را مقدار دهی کنید.
در زیر یک مثال ساده در خصوص فیلد ناشناس زدیم :
1package main
2
3import "fmt"
4
5type employee struct {
6 string
7 age int
8 salary int
9}
10
11func main() {
12 emp := employee{string: "Sam", age: 31, salary: 2000}
13 //Accessing a struct field
14 n := emp.string
15 fmt.Printf("Current name is: %s\n", n)
16 //Assigning a new value
17 emp.string = "John"
18 fmt.Printf("New name is: %s\n", emp.string)
19}
2.2.8 تعریف ساختار تو در تو (nested) #
یکی دیگر از امکانات ساختار در زبان گو بحث ساختار تو در تو می باشد. که در زیر مثالی که زدیم ساختار address را داخل employee قرار دادیم :
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9 address address
10}
11
12type address struct {
13 city string
14 country string
15}
16
17func main() {
18 address := address{city: "London", country: "UK"}
19 emp := employee{name: "Sam", age: 31, salary: 2000, address: address}
20 fmt.Printf("City: %s\n", emp.address.city)
21 fmt.Printf("Country: %s\n", emp.address.country)
22}
توجه کنید شما طبق روش زیر می توانید به فیلدهای تو در تو دسترسی داشته باشید :
2.2.9 تعریف یک ساختار عمومی یا خصوصی (Public/Private) #
در زبان گو چیزی به عنوان کلمه کلیدی public یا private جهت تعیین وضعیت دسترسی به بیرون ندارد ولی در عوض کامپایلر گو براساس حرف بزرگ یا کوچک عنوان ساختار یا سایر تایپ ها تشخیص می دهد تایپ شما عمومی است یا خصوصی و در صورتیکه شما حرف اول را کوچک قرار دهید به کامپایلر دارید میگید این تایپ از بیرون این پکیج دسترسی ندارد.
برای اطلاعات بیشتر بهتره به بخش کپسوله سازی مراجعه کنید.
2.2.10 مقایسه ساختارها #
در زبان گو خیلی ساده می توانید ساختارها را بر اساس عنوان فیلد و تایپ و مقدارشان مقایسه کنید. اما باید توجه کنید ساختارها فقط براساس تایپ هایی که در زیر معرفی کردیم امکان مقایسه را خواهند داشت :
- boolean
- numeric
- string
- pointer
- channel
- interface types
- structs
- array
اما ۳ تایپ زیر امکان مقایسه را از بین می برد :
- Slice
- Map
- Function
1package main
2
3import "fmt"
4
5type employee struct {
6 name string
7 age int
8 salary int
9}
10
11func main() {
12 emp1 := employee{name: "Sam", age: 31, salary: 2000}
13 emp2 := employee{name: "Sam", age: 31, salary: 2000}
14 if emp1 == emp2 {
15 fmt.Println("emp1 annd emp2 are equal")
16 } else {
17 fmt.Println("emp1 annd emp2 are not equal")
18 }
19}