ساختار یا 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 (  
    &quotfmt&quot
)

type Employee struct {  
    firstName string
    lastName  string
    age       int
    salary    int
}

func main() {

    //creating struct specifying field names
    emp1 := Employee{
        firstName: &quotAli&quot,
        age:       25,
        salary:    500,
        lastName:  &quotMehdi&quot,
    }

    //creating struct without specifying field names
    emp2 := Employee{&quotMehdi&quot, &quotAli&quot, 29, 800}

    fmt.Println(&quotEmployee 1&quot, emp1)
    fmt.Println(&quotEmployee 2&quot, emp2)
}

ساختار emp2 با حذف نام فیلدها تعریف می شود. در این حالت، لازم است که ترتیب فیلدها همان چیزی باشد که در struct مشخص شده است. لطفاً از استفاده از این سینتکس خودداری کنید زیرا تشخیص اینکه کدام مقدار برای کدام قسمت است دشوار است. ما این قالب را در اینجا گفتیم تا بدانید که این نیز یک سینتکس معتبر است :)

ایجاد استراکت anonymous

بدون ایجاد نوع داده جدید(تعریف نام برای struct)، می توان یک ساختار تعریف کرد. به این نوع از struct ها anonymous (ناشناس) گفته می شود.

package main

import (  
    &quotfmt&quot
)

func main() {  
    emp3 := struct {
        firstName string
        lastName  string
        age       int
        salary    int
    }{
        firstName: &quotAli&quot,
        lastName:  &quotIrani&quot,
        age:       49,
        salary:    50000,
    }

    fmt.Println(&quotEmployee 3&quot, emp3)
}

متغیر emp3 از نوع ساختار anonymous تعریف شده است. همانطور که قبلاً اشاره کردیم، این ساختار ناشناس نامیده می شود زیرا فقط متغیر جدیدی ایجاد می کند و هیچ نوع struct جدیدی مانند named struct را تعریف نمی کند.
خروجی این برنامه :

Employee 3 {Ali Irani 49 50000}


دسترسی به فیلد های یک struct

عملگر نقطه . برای دسترسی به فیلدهای یک ساختار استفاده می شود.

fmt.Println(&quotFirst Name:&quot, emp3.firstName)

در برنامه فوق به فیلد firstName از ساختار emp3 دسترسی پیدا میکنیم.

مقدار صفر(zero) یا تعریف نشده یک ساختار

وقتی یک ساختار تعریف می شود و به طور واضح با هیچ مقداری مقداردهی اولیه نمی شود، به فیلدهای ساختار به طور پیش فرض مقادیر صفر آنها اختصاص داده می شود.

var emp4 Employee //zero valued struct
 fmt.Println(&quotFirst Name:&quot, emp4.firstName)
 fmt.Println(&quotLast Name:&quot, emp4.lastName)
 fmt.Println(&quotAge:&quot, emp4.age)
 fmt.Println(&quotSalary:&quot, emp4.salary)

برنامه فوق emp4 را تعریف می کند اما با هیچ مقداری مقداردهی اولیه نمی شود. از این رو به firstName و lastName مقادیر صفر رشته ای که یک رشته خالی است "" اختصاص داده می شود و به age ، salary مقادیر صفر int که 0 است اختصاص داده می شود. خروجی این برنامه :

First Name:  
 Last Name:  
 Age: 0  
 Salary: 0

همچنین می توان مقادیر را برای برخی از فیلدها مشخص کرد و بقیه را نادیده گرفت و مقدار بهشون نداد. در این حالت هم به فیلدهای نادیده گرفته شده مقادیر صفر اختصاص داده می شود.

اشاره گرها (Pointers) به یک ساختار

همچنین می توان اشاره گر(pointer) برای یک ساختار را ایجاد کرد.
emp5 := &Employee{
        firstName: &quotMehdi&quot,
        lastName:  &quotYounesi&quot,
        age:       30,
        salary:    6000,
}
fmt.Println(&quotFirst Name:&quot, (*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: &quotMehdi&quot,
        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: &quotNaveen&quot,
        age:  50,
        address: Address{
            city:  &quotChicago&quot,
            state: &quotIllinois&quot,
        },
    }
}

شاید سوال براتون پیش باید که حالا نحوه دسترسی بهاطلاعات آدرس در این ساختار 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 (  
    &quotfmt&quot
)

type Address struct {  
    city  string
    state string
}
type Person struct {  
    name string
    age  int
    Address
}

func main() {  
    p := Person{
        name: &quotMehdi&quot,
        age:  30,
        Address: Address{
            city:  &quotIstanbul&quot,
            state: &quotAvcılar&quot,
        },
    }

    fmt.Println(&quotName:&quot, p.name)
    fmt.Println(&quotAge:&quot, p.age)
    fmt.Println(&quotCity:&quot, p.city)   //city is promoted field
    fmt.Println(&quotState:&quot, p.state) //state is promoted field
} 

مقایسه برابری structها

ساختارها از نوع value type هستند و اگر هر یک از فیلد های آنها قابل مقایسه باشد، قابل مقایسه هستند. وقتی دو متغیر از نوع struct با هم برابر هستند که تمام فیلدهای آن ها با هم برابر باشند.

package main

import (  
    &quotfmt&quot
)

type name struct {  
    firstName string
    lastName  string
}

func main() {  
    name1 := name{
        firstName: &quotSteve&quot,
        lastName:  &quotJobs&quot,
    }
    name2 := name{
        firstName: &quotSteve&quot,
        lastName:  &quotJobs&quot,
    }
    if name1 == name2 {
        fmt.Println(&quotname1 and name2 are equal&quot)
    } else {
        fmt.Println(&quotname1 and name2 are not equal&quot)
    }

    name3 := name{
        firstName: &quotSteve&quot,
        lastName:  &quotJobs&quot,
    }
    name4 := name{
        firstName: &quotSteve&quot,
    }

    if name3 == name4 {
        fmt.Println(&quotname3 and name4 are equal&quot)
    } else {
        fmt.Println(&quotname3 and name4 are not equal&quot)
    }
}

خروجی قطعه کد فوق:

name1 and name2 are equal  
 name3 and name4 are not equal

متغیرهای struct در صورت داشتن فیلد هایی که قابل مقایسه نیستند، قابل مقایسه نخواهند بود.

نتیجه گیری

عالی! اکنون آماده استفاده از struct در گولنگ هستید. همه مبانی مانند اعلان، مقداردهی اولیه، ساختار و فیلدهای anonymous و دسترسی به فیلد های ساختار را توضیح دادم. ما همچنین به چگونگی مقایسه دو struct نگاه کردیم و حتی ساختار تو در تو را اجرا کردیم. دیگه موردی برای یادگیری در مورد struct در گولنگ باقی نماند.

با تشکر از شما برای خواندن این مطلب، لطفا نظرات و انتقادات خودتون را کامنت کنید.