توسعه دهنده Back-End
ساختار یا struct در گولنگ چیست ؟
معرفی
یک struct (ساختار) به مجموعهای از فیلدهای مرتب با هم که یک واحد منسجم (unit) را تشکیل میدهند اشاره دارد. به هر عضو از این unit یک فیلد field گفته میشود. ساختار یک نوع تعریف شده توسط کاربر (user-defined) است که در مواردی که منطقی باشد که داده ها را به جای مقادیر جداگانه در یک واحد قرار دهیم استفاده می شود.
به عنوان مثال، یک کارمند دارای نام، نام خانوادگی و سن است. منطقی است که این سه ویژگی را در یک ساختار واحد به نام Employee گروه بندی کنید.
نحوه تعریف استراکت
type Employee struct {
firstName string
lastName string
age int
}
قطعه کد فوق یک type ساختار از ׄEmployee با زمینه های firstName ، LastName و age تعریف می کند. به ساختار Employee فوق به این دلیل named struct نامیده می شود که نوع داده جدیدی به نام Employee با استفاده از Employee ایجاد می کند.
مقدار دهی اولیه
قطعه کد زیر را ببینید، ساختار emp1 با تعیین مقدار برای هر نام فیلد تعریف می شود. هنگام اعلام نوع ساختار، لزوماً ترتیب فیلدها مانند ترتیب نام فیلدها نیست. در این مورد ما موقعیت فیلد lastName را تغییر داده و به انتها منتقل کرده ایم. این بدون مشکل کار خواهد کرد.
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: "Ali",
age: 25,
salary: 500,
lastName: "Mehdi",
}
//creating struct without specifying field names
emp2 := Employee{"Mehdi", "Ali", 29, 800}
fmt.Println("Employee 1", emp1)
fmt.Println("Employee 2", emp2)
}
ساختار emp2 با حذف نام فیلدها تعریف می شود. در این حالت، لازم است که ترتیب فیلدها همان چیزی باشد که در struct مشخص شده است. لطفاً از استفاده از این سینتکس خودداری کنید زیرا تشخیص اینکه کدام مقدار برای کدام قسمت است دشوار است. ما این قالب را در اینجا گفتیم تا بدانید که این نیز یک سینتکس معتبر است :)
ایجاد استراکت anonymous
بدون ایجاد نوع داده جدید(تعریف نام برای struct)، می توان یک ساختار تعریف کرد. به این نوع از struct ها anonymous (ناشناس) گفته می شود.
package main
import (
"fmt"
)
func main() {
emp3 := struct {
firstName string
lastName string
age int
salary int
}{
firstName: "Ali",
lastName: "Irani",
age: 49,
salary: 50000,
}
fmt.Println("Employee 3", emp3)
}
متغیر emp3 از نوع ساختار anonymous تعریف شده است. همانطور که قبلاً اشاره کردیم، این ساختار ناشناس نامیده می شود زیرا فقط متغیر جدیدی ایجاد می کند و هیچ نوع struct جدیدی مانند named struct را تعریف نمی کند.
خروجی این برنامه :
Employee 3 {Ali Irani 49 50000}
دسترسی به فیلد های یک struct
عملگر نقطه . برای دسترسی به فیلدهای یک ساختار استفاده می شود.
fmt.Println("First Name:", emp3.firstName)
در برنامه فوق به فیلد firstName از ساختار emp3 دسترسی پیدا میکنیم.
مقدار صفر(zero) یا تعریف نشده یک ساختار
وقتی یک ساختار تعریف می شود و به طور واضح با هیچ مقداری مقداردهی اولیه نمی شود، به فیلدهای ساختار به طور پیش فرض مقادیر صفر آنها اختصاص داده می شود.
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 را تعریف می کند اما با هیچ مقداری مقداردهی اولیه نمی شود. از این رو به firstName و lastName مقادیر صفر رشته ای که یک رشته خالی است "" اختصاص داده می شود و به age ، salary مقادیر صفر int که 0 است اختصاص داده می شود. خروجی این برنامه :
First Name:
Last Name:
Age: 0
Salary: 0
همچنین می توان مقادیر را برای برخی از فیلدها مشخص کرد و بقیه را نادیده گرفت و مقدار بهشون نداد. در این حالت هم به فیلدهای نادیده گرفته شده مقادیر صفر اختصاص داده می شود.
اشاره گرها (Pointers) به یک ساختار
همچنین می توان اشاره گر(pointer) برای یک ساختار را ایجاد کرد.
emp5 := &Employee{
firstName: "Mehdi",
lastName: "Younesi",
age: 30,
salary: 6000,
}
fmt.Println("First Name:", (*emp5).firstName)
دقت کنید در برنامه فوق emp5 یک اشاره گر به ساختار Employee است . *emp5.firstName نحوه دستیابی به فیلد firstName از ساختار emp5 است. (میدونم پرانتزها رو نگذاشتم جون تو ویرگول هر کاری کردم درست الاین نشد)
زبان Go به ما این امکان را می دهد تا از emp5.firstName به جای استفاده از استنباط صریح (همانند مدل بالا) برای دسترسی به فیلد firstName استفاده کنیم.
فیلد های ناشناس (Anonymous)
می توان با فیلدهایی که فقط یک نوع بدون نام فیلد در آن قرار دارند، struct را ایجاد کرد. این نوع فیلدها را فیلدهای anonymous می نامند. قطعه کد زیر یک ساختار Person ایجاد می کند که دارای دو فیلد ناشناس string و int است.
type Person struct {
string
int
}
همانطور که می بینید فیلدهای ناشناس نام مشخصی ندارند اما به طور پیش فرض نام فیلد ناشناس نام نوع آن است. به عنوان مثال در مورد ساختار Person در بالا، اگر چه فیلدها ناشناس هستند، اما به طور پیش فرض آنها نام نوع فیلدها را می گیرند. بنابراین ساختار Person دارای ۲ فیلد با نام های string و int است.
p1 := Person{
string: "Mehdi",
int: 50,
}
fmt.Println(p1.string)
fmt.Println(p1.int)
خروجی:
Mehdi
50
ساختارهای Nested
این امکان وجود دارد که مقدار فیلد struct به صورت struct تعریف شود که در این صورت به آنها ساختارهای تو در تو یا nested گفته می شود. در مثال زیر اطلاعات آدرس در ساختار اطلاعات خود شخص گنجانده شده است!
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",
},
}
}
شاید سوال براتون پیش باید که حالا نحوه دسترسی بهاطلاعات آدرس در این ساختار nested چطور خواهد بود؟ خیلی راحت مثل حالت عادی
p.address.city
// and
p.address.state
یک سری فیلد داریم که بهشون میگن Promoted fields
فیلدهایی که به یک فیلد ساختار ناشناس در یک ساختار تعلق دارند، فیلدهای promoted گفته می شود زیرا می توان به آنها دسترسی پیدا کرد انگار که به ساختار متعلق به فیلد anonymous تعلق دارند. یعنی چی؟
برای اینکار کافی بود که در مثال قبلی اسمی برای ساختار nested خود تعریف نکنیم. یعنی به صورت زیر:
type Person struct {
name string
age int
Address
}
در قطعه کد فوق، Person ساختار دارای یک فیلد ناشناس Address است که یک ساختار است. اکنون فیلدهای Address یعنی city و state را فیلدهای ارتقا یافته (promoted) می نامند زیرا حالا می توان به گونه ای که مستقیماً در خود ساختار Person اعلام شده باشد به آنها دسترسی داشت.
package main
import (
"fmt"
)
type Address struct {
city string
state string
}
type Person struct {
name string
age int
Address
}
func main() {
p := Person{
name: "Mehdi",
age: 30,
Address: Address{
city: "Istanbul",
state: "Avcılar",
},
}
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
}
مقایسه برابری structها
ساختارها از نوع value type هستند و اگر هر یک از فیلد های آنها قابل مقایسه باشد، قابل مقایسه هستند. وقتی دو متغیر از نوع struct با هم برابر هستند که تمام فیلدهای آن ها با هم برابر باشند.
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")
}
}
خروجی قطعه کد فوق:
name1 and name2 are equal
name3 and name4 are not equal
متغیرهای struct در صورت داشتن فیلد هایی که قابل مقایسه نیستند، قابل مقایسه نخواهند بود.
نتیجه گیری
عالی! اکنون آماده استفاده از struct در گولنگ هستید. همه مبانی مانند اعلان، مقداردهی اولیه، ساختار و فیلدهای anonymous و دسترسی به فیلد های ساختار را توضیح دادم. ما همچنین به چگونگی مقایسه دو struct نگاه کردیم و حتی ساختار تو در تو را اجرا کردیم. دیگه موردی برای یادگیری در مورد struct در گولنگ باقی نماند.
با تشکر از شما برای خواندن این مطلب، لطفا نظرات و انتقادات خودتون را کامنت کنید.
مطلبی دیگر از این انتشارات
Go Developer Roadmap part 1
مطلبی دیگر از این انتشارات
پوینتر ها در گو | آشنایی و درک بهتر آنها
مطلبی دیگر از این انتشارات
کتابخانه های استاندارد در Go (بخش اول)