1 سٹرکچ کی ابتدائی معلومات

Go زبان میں، ایک سٹرکچ ایک مجموعی ڈیٹا ٹائپ ہے جو مختلف یا ممکنہ قسم کے ڈیٹا کو ایک واحد شہرت میں جمع کرنے کے لئے استعمال ہوتا ہے۔ سٹرکچ Go میں اہم کردار ادا کرتا ہے کیونکہ یہ عام طریقے سے بنائی گئی سٹرکچر کی عملی بنیاد میں ایک بنیادی پہلو کی طرح کام کرتا ہے، اگرچہ روایتی طرزِ عمل کی تھوڑی سی مختلف ہوتی ہے۔

سٹرکچ کی ضرورت مندرجہ ذیل پہلوؤں سے پیش آتی ہے:

  • مضبوط تضاد کے ساتھ متعلق متغیرات کو ایک ساتھ منظم کرکے کوڈ کی ہندسوں کو بہتر بنانے کا ذریعہ فراہم کرنا۔
  • "کلاسیں" نقل کرنے کا ایک ذریعہ فراہم کرنا، جو انکی بنا پر موڈیل کو پوری طرح سے بنانے اور جمع کرنے کی خصوصیات فراہم کرتا ہے۔
  • جب ڈیٹا ساختوں جیسے کے JSON، ڈیٹا بیس ریکارڈ وغیرہ سے بات چیت کرتے ہیں تو سٹرکچ شاھدانہ نقشہ ورکٹول فراہم کرتا ہے۔

سٹرکچ کے ساتھ ڈیٹا کو منظم کرنا واقعی دنیا کے اشیاء کے نمونوں کی صاف ترین نمائندگی فراہم کرتا ہے جیسے کہ صارفین، آرڈرز وغیرہ۔

پیکیج main

استعمال "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    // شخص کی قسم کی متغیر تخلیق کریں
    p := شخص{"الس", 30}

    // سٹرکٹ کے رکن تک رسائی حاصل کریں
    fmt.Println("نام:", p.Name)
    fmt.Println("عمر:", p.Age)

    // رکنیں کی قیمتوں کو تبدیل کریں
    p.Name = "باب"
    p.Age = 25

    // تبدیل شدہ رکنیں دوبارہ حاصل کریں
    fmt.Println("\nاپ ڈیٹ کیا گیا نام:", p.Name)
    fmt.Println("اپ ڈیٹ کی عمر:", p.Age)
}

اس مثال میں ہم نے پہلے Person سٹرکچر کو دو رکنی متغیرات، Name اور Age کے ساتھ تعریف کی ہے۔ پھر ہم نے اس سٹرکچر کا ایک مثال تخلیق کی اور دکھایا ہے کہ ان رکنوں کو کیسے پڑھا یا تبدیل کیا جا سکتا ہے۔

5 سٹرکچر کی تشکیل اور ایمبیڈنگ

سٹرکچر صرف اکیلے ہی نہیں رہ سکتے بلکہ ان کو آپس میں ملا کر زیادہ پیچیدہ ڈیٹا ساخت کیا جا سکتا ہے۔

5.1 نامعلوم سٹرکچر

ایک نامعلوم سٹرکچر نے نئے قسم کا صریح طور پر اعلان نہیں کرتا ہے، بلکہ بدلہ ہوا سٹرکچر کی تعریف سیدھا استعمال کرتا ہے۔ یہ فائدہ مند ہوتا ہے جب آپ کو ایک بار سٹرکچر تخلیق کرنا ہو اور بس اس کا استعمال کرنا ہو، ان غیر ضروری قسموں کا تخلیق کرنے سے بچا جا سکتا ہے۔

مثال:

پیکیج main

استعمال "fmt"

func main() {
    // ایک نامعلوم سٹرکچر کی تعریف اور ابتدائیت
    شخص := struct {
        Name string
        Age  int
    }{
        Name: "ایو",
        Age:  40,
    }

    // نامعلوم سٹرکچر کے رکن تک رسائی حاصل کریں
    fmt.Println("نام:", شخص.Name)
    fmt.Println("عمر:", شخص.Age)
}

اس مثال میں، ہم نے ایک نیا قسم کا اعلان نہیں کیا، بلکہ ہم نے براہ راست ایک سٹرکچر کی تعریف کی اور اس کا ایک مثال تخلیق کیا۔ یہ مثال دکھاتی ہے کہ کس طرح ایک نامعلوم سٹرکچر کو کیسے ابتدائیت کیا جاتا ہے اور اس کے رکنوں تک کیسے رسائی حاصل کی جاتی ہے۔

5.2 سٹرکچر ایمبیڈنگ

سٹرکچر ایمبیڈنگ میں ایک سٹرکچر کو دوسرے سٹرکچر کے رکن کے طور پر چھپانا شامل ہے۔ یہ ہمیں زیادہ پیچیدہ ڈیٹا ماڈل بنانے کی اجازت دیتا ہے۔

مثال:

پیکیج main

استعمال "fmt"

// سٹرکچر ایڈریس کی تعریف
type Address struct {
    City    string
    Country string
}

// Address سٹرکچر کو Person سٹرکچر میں ایمبیڈ کریں
type Person struct {
    Name    string
    Age     int
    Address Address
}

func main() {
    // ایک شخص کا مثال تخلیق کریں
    p := Person{
        Name: "چارلی",
        Age:  28,
        Address: Address{
            City:    "نیو یارک",
            Country: "یو ایس اے",
        },
    }

    // ایمبیڈ شدہ سٹرکچر کے رکن تک رسائی حاصل کریں
    fmt.Println("نام:", p.Name)
    fmt.Println("عمر:", p.Age)
    // ایڈریس سٹرکچر کے رکن تک رسائی حاصل کریں
    fmt.Println("سٹی:", p.Address.City)
    fmt.Println("ملک:", p.Address.Country)
}

اس مثال میں، ہم نے Address سٹرکچر کی تعریف کی ہے اور اسے Person سٹرکچر میں ایمبیڈ کیا ہے۔ جب Person کا ایک مثال تخلیق کیا گیا ہے، تو ہم نے ساتھ ہی Address کا ایک مثال بھی تخلیق کیا ہے۔ ہم دونوں مثالات کے رکنوں تک پہنچ سکتے ہیں۔

6 سٹرکچر کے میتھڈز

آبجیکٹ آرائیانی پروگرامنگ (OOP) خصوصیات کو سٹرکچر میتھڈز کے ذریعے نافذ کیا جا سکتا ہے۔

6.1 میتھڈز کے بنیادی تصورات

go زبان میں، ہالانکہ کلاسز اور آبجیکٹس کا روایتی تصور نہیں ہے، لیکن مشابہ OOP خصوصیات سٹرکچر میتھڈز کو بائنڈ کر کے حاصل کیا جا سکتا ہے۔ سٹرکچر میتھڈ ایک خصوصی قسم کی فنکشن ہوتا ہے جو کسی خصوصی قسم کے ساتھ (یا اس قسم کے پائنٹر کے ساتھ) منسلک ہوتا ہے، جس سے وہ قسم اپنے خود کے میتھڈز کا مخصوص سیٹ رکھ سکتی ہے۔

### 6.2 ویلیو رسیورز اور پوائنٹر رسیورز

میتھڈز کو ریسیور کی قسم کے بنیاد پر ویلیو ریسیورز اور پوائنٹر ریسیورز میں تقسیم کیا جا سکتا ہے۔ ویلیو ریسیورز سٹرکٹ کا ایک کاپی استعمال کرتے ہیں تاکہ میتھڈ کو کال کیا جا سکے، جبکہ پوائنٹر ریسیورز سٹرکٹ کے پوائنٹر کا استعمال کرتے ہیں اور اصل سٹرکٹ کو ترتیب دے سکتے ہیں۔

```go
// ویلیو ریسیور کے ساتھ ایک میتھڈ کو تعریف کریں
func (r Rectangle) Perimeter() float64 {
    return 2 * (r.length + r.width)
}

// پوائنٹر ریسیور کے ساتھ ایک میتھڈ کو تعریف کریں، جو سٹرکچر کو ترتیب دے سکتی ہے
func (r *Rectangle) SetLength(newLength float64) {
    r.length = newLength // اصل سٹرکچر کی قیمت کو ترتیب دے سکتا ہے
}

اوپر دیئے گئے مثال میں، Perimeter ایک ویلیو ریسیورز میتھڈ ہے، اسے کال کرنا سٹرکٹ Rectangle کی قیمت کو تبدیل نہیں کرے گا۔ البتہ، SetLength ایک پوائنٹر ریسیور میتھڈ ہے، اور اس میتھڈ کو کال کرنے سے اصل Rectangle مثال کو متاثر کرے گا۔

6.3 میتھڈ بلائی

آپ سٹرکٹ کے میتھڈز کو سٹرکٹ متغیر اور اس کے پوائنٹر کا استعمال کرتے ہوئے بلا سکتے ہیں۔

func main() {
    rect := Rectangle{length: 10, width: 5}

    // ویلیو ریسیور کے ساتھ میتھڈ کو کال کریں
    fmt.Println("Area:", rect.Area())

    // ویلیو ریسیور کے ساتھ میتھڈ کو کال کریں
    fmt.Println("Perimeter:", rect.Perimeter())

    // پوائنٹر ریسیور کے ساتھ میتھڈ کو کال کریں
    rect.SetLength(20)

    // دوبارہ ویلیو ریسیور کے ساتھ میتھڈ کو کال کریں، نوٹ کریں کہ لمبائی میں ترتیب ہو گئی ہے
    fmt.Println("After modification, Area:", rect.Area())
}

جب آپ ایک میتھڈ کو پوائنٹر کے ساتھ کال کرتے ہیں، Go خود بخود اقدار اور پوائنٹروں کے درمیان تبدیلی کا سلسلہ منظور کرتا ہے، بے شک اگر آپ کا میتھڈ ویلیو ریسیور یا پوائنٹر ریسیور کے ساتھ تعریف کیا گیا ہو۔

6.4 ریسیور قسم کا انتخاب

میتھڈز کی تعریف کرتے وقت، آپ کو سرعتسازی کے بنیاد پر ویلیو ریسیور یا پوائنٹر ریسیور کا استعمال کرنا چاہئے۔ یہاں کچھ عام ہدایات ہیں:

  • اگر میتھڈ سڑکچر کے مواد میں ترتیب کرنے کی ضرورت ہو تو، پوائنٹر ریسیور کا استعمال کریں۔
  • اگر سڑکچر بڑا ہے اور کاپی کرنے کی لاگت زیادہ ہے تو، پوائنٹر ریسیور کا استعمال کریں۔
  • اگر آپ چاہتے ہیں کہ میتھڈ وصول کنندہ کی قیمت کو ترتیب کرے، تو پوائنٹر ریسیور کا استعمال کریں۔
  • فطرتاً، بڑے سڑکچر کے لئے حتمی طور پر کاپی کرنے کی ضرورت نہیں ہونے کی صورت میں، بھی سرعت کی بنیاد پر پوائنٹر ریسیور کا استعمال منطقی ہے۔
  • چھوٹے سڑکچر کے لئے، یا جب صرف ڈیٹا کو پڑھنا ہو بغیر ترتیب کرنے کی ضرورت کے، ویلیو ریسیور عام طور پر زیادہ سادہ اور موثر ہوتا ہے۔

سڑکچر میتھڈز کے ذریعہ، Go میں کچھ وسائل کو ترتیب دینے اور میتھڈز کو منظم کرنے کی کافی صلاحیت فراہم کرتا ہے، یہ موقع میں گو میں اوبجیکٹ آرائی فقہ کی کچھ خصوصیات کا نقل کرتا ہے، جیسے کہ محفوظ کرنا اور میتھڈز۔ یہ طریقہ میں Go میں کانسپٹ آف آبجکٹس کو سادہ بناتا ہے جبکہ کافی صلاحیت فراہم کرتا ہے کہ متعلقہ فعلوں کو منظم کیا جا سکے۔

7 سڑکچر اور JSON سیریلائزیشن

Go میں عام طور پر چاہیے ہوتا ہے کہ سڑکچر کو جیسن فارمیٹ میں سیریلائز کیا جائے تاکہ نیٹ ورک میں بھیجنے یا تشکیل فائل کے طور پر استعمال کیا جائے۔ اسی طرح، ہمیں جیسن کو سڑکچر ایکسٹنسز میں ٹھیک کرنا بھی ضرورت ہوتی ہے۔ Go میں encoding/json پیکیج اس فعالیت کی فراہمی کرتا ہے۔

یہاں، ہم ایک مثال دیتے ہیں کہ کیسے ایک سڑکچر کو جیسن میں تبدیل کیا جاتا ہے اور جیسن کو سڑکچر ایکسٹنسز میں تبدیل کیا جاتا ہے:

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

// شخص کے سٹرکچر کو تعریف کریں اور جیسن ٹیگز کا استعمال کریں تاکہ سٹرکچر فیلڈز اور جیسن فیلڈ ناموں کے درمیان مینپلنگ تعریف کیا جائے
type Person struct {
	Name   string   `json:"name"`
	Age    int      `json:"age"`
	Emails []string `json:"emails,omitempty"`
}

func main() {
	// شخص کا ایک نیا مثال تیار کریں
	p := Person{
		Name:   "John Doe",
		Age:    30,
		Emails: []string{"[email protected]", "[email protected]"},
	}

	// جیسن میں تبدیل کریں
	jsonData, err := json.Marshal(p)
	if err != nil {
		log.Fatalf("JSON marshaling failed: %s", err)
	}
	fmt.Printf("JSON format: %s\n", jsonData)

	// سڑکچر میں تبدیل کریں
	var p2 Person
	if err := json.Unmarshal(jsonData, &p2); err != nil {
		log.Fatalf("JSON unmarshaling failed: %s", err)
	}
	fmt.Printf("Recovered Struct: %#v\n", p2)
}

اوپر دی گئی کوڈ میں، ہم نے Person سڑکچر کو تعریف کیا، جس میں "omitempty" آپشن کے ساتھ ایک سلائس ٹائپ فیلڈ شامل ہے۔ یہ آپشن وضاحت کرتا ہے کہ اگر فیلڈ خالی ہو یا غائب ہو تو یہ جیسن میں شامل نہیں کیا جائے گا۔

ہم نے json.Marshal فنکشن کا استعمال کر کے ایک سڑکچر مثال کو جیسن میں سیریلائز کیا، اور json.Unmarshal فنکشن کا استعمال کر کے جیسن ڈیٹا کو سڑکچر مثال میں تبدیل کیا۔

8 سٹرکٹس میں ماہر موضوعات

8.1 سٹرکٹس کی موازنہ

Go میں، دو سٹرکٹس کے موازنہ کرنا اجازت دیا گیا ہے، لیکن یہ موازنہ سٹرکٹس کے فیلڈ کی قیمتوں پر مبنی ہے۔ اگر تمام فیلڈ کی قیمتیں برابر ہوں، تو دو سٹرکٹس کے موازنہ کیا جاتا ہے۔ یہ بات یاد رہے کہ تمام فیلڈ کی قسمیں موازنہ کرنے کے قابل نہیں ہوتیں۔ مثال کے طور پر، ایک سٹرکٹس جو مواد رکھتی ہے، اسے براہِ راست موازنہ نہیں کیا جا سکتا۔

نیچے دی گئی مثال میں سٹرکٹس کا موازنہ دکھایا گیا ہے:

package main

import "fmt"

type Point struct {
	X, Y int
}

func main() {
	p1 := Point{1, 2}
	p2 := Point{1, 2}
	p3 := Point{1, 3}

fmt.Println("p1 == p2:", p1 == p2) // Output: p1 == p2: true
fmt.Println("p1 == p3:", p1 == p3) // Output: p1 == p3: false
}

اس مثال میں، p1 اور p2 برابر فرض کیے جاتے ہیں کیونکہ ان کی تمام فیلڈ قیمتیں یکساں ہیں۔ اور p3 p1 کے برعکس ہے کیونکہ Y کی قیمت مختلف ہے۔

8.2 سٹرکٹس کا کاپی

Go میں، سٹرکٹس کے مواد کو کسی مستقبل کردار کرنا ممکن ہے۔ یہ کہ کیا یہ کاپی گہری کاپی ہوگی یا سطحی کاپی ہوگی، یہ سٹرکٹس کے فیلڈ کی قسموں پر منحصر ہوتا ہے۔

اگر سٹرکٹس میں صرف بنیادی قسمیں ہیں (جیسے int، string، وغیرہ) تو کاپی گہری ہوگی۔ اگر سٹرکٹس میں حوالہ قسمیں ہوتی ہیں (جیسے سلائس، نقشے، وغیرہ)، تو کاپی سطحی ہوگی، اور اصل اور نیا کاپی شدہ وقت کی حوالے کی قسموں کی یاداشت کرتے ہیں۔

نیچے دی گئی مثال میں، ایک سٹرکچر کی کاپی دکھائی گئی ہے:

package main

import "fmt"

type Data struct {
Numbers []int
}

func main() {
// ساختہ سٹرکچر کا ابتدائی مثال
original := Data{Numbers: []int{1, 2, 3}}

// سکیت کا ابتدائی مثال
copied := original

// کاپی شدہ سلائس کے عناصر کو ترتیب دے دیں
copied.Numbers[0] = 100

// اصل اور نقل شدہ مثالوں کے عناصر دیکھیں
fmt.Println("Original:", original.Numbers) // Output: Original: [100 2 3]
fmt.Println("Copied:", copied.Numbers) // Output: Copied: [100 2 3]

اس مثال میں دکھایا گیا ہے کہ original اور copied مثالیں ایک ہی سلائس کا حصہ ہیں، لہذا copied میں سلائس کی ڈیٹا کو ترتیب دینا بھی original میں سلائس کی ڈیٹا پر اثرانداز کریگا۔

ان مسائل سے بچنے کے لئے، آپ copy کا استعمال کرکے سچی گہری کاپی حاصل کرسکتے ہیں:

newNumbers := make([]int, len(original.Numbers))
copy(newNumbers, original.Numbers)
copied := Data{Numbers: newNumbers}

اس طریقے سے، copied میں کوئی ترمیم original پر اثرانداز نہیں ہوگی۔