Go öğrenme yolculuğumda, hem kendime hem de ilgilenenlere kaynak olması için yazmış olduğum bir yazıdan daha herkese merhaba 🥳
Başlık yeterince kafa karıştırdıysa 🤯, bu yazıda Go’daki structures, pointers kavramlarından ve birlikte kullanımlarından bahsediyor olacağımı belirtip başlayabilirim.
Structs/Structures
Object-Oriented Programming’in aksine, Go’da class-object mimarisi yoktur. Bunun yerine, karmaşık veri yapılarını bir arada tutmayı/gruplandırmayı sağlayan struct’lar vardır.
Struct tanımlamak için, isminin önüne type ve sonuna da struct keyword’lerini eklememiz gerekir. Bir struct int, string türündeki alanlardan oluşabileceği gibi içerisinde farklı türde struct alanlar da bulundurabilir.
Struct’lar için fonksiyon tanımlamaları da yapabiliriz. Örneğin aşağıda isim güncellemesi yapan ve Person tipinde bir receiver fonksiyon tanımladık.
FirstName alanı “Ada” olarak initiliaze edilen person’ı, FirstName’i “Augusta” olarak güncellediğimiz dolayısıyla “Augusta Lovelace” çıktısını vermesini beklediğimiz kodu çalıştırdığımızda (⌛️ heyecanı arttırmak için burada biraz oyalayayım sizi tamam yeterli⏳) console ekranında “Ada Lovelace” yazdığını görüp ufak bir şaşkınlık yaşarız.
Pass By Value
FirstName alanının niçin fonksiyona gönderilen parametreyle değiştirilmediğine geçmeden önce bilgisayarlarımızda bulunun RAM’in çalışma mantığıyla ilgili ufak bir hatırlatma yapmak istiyorum.
Bilgisayardaki belleği birçok küçük kutudan oluşuyormuş gibi düşünelim. Bu küçük kutuların birbirinden farklı adresleri vardır. Bu yüzden yazdığımız program “Bilgisayarın belleğinden bazı bilgileri almak istiyorum.” dediği bir adres bulur ve değeri o adresten alır.
Şimdi yukarıda yazdığımız programın bilgisayarımızdaki RAM ile nasıl çalıştığını inceleyelim. İlk olarak Person tipinde bir struct oluşturduğumuzda ve onu person değişkenine atadığımızda Go struct’ı oluşturur. Daha sonra belleğe gider ve bu değeri yerleştirebileceği bir alan seçer. Böylece person struct’ının şekildeki 0001 adresine konumlandırıldığını düşünebiliriz. person doğrudan bu küçük kutuyu işaret ediyor. Ve person değerini programda yazdırdığımızda her zaman tam olarak 0001 adresindekini değerini göreceğiz.
Şimdi person.UpdateNameInformation() fonksiyonu çağırdığımızda neler olacağını inceleyelim. İşler burada biraz tuhaflaşıyor. Go “pass by value” yaklaşımına sahip bir dildir. Bir fonksiyona bir değer/struct ilettiğimizde Go o struct’un içerisindeki tüm verileri kopyalar ve belleğin içerisinde yeni bir kutuya yerleştirir (0003). Bu yüzden person’a yeni bir FirstName değeri ilettiğimizde person’un FirstName’i 0001 adresinde “Ada” olarak var olmaya devam eder. UpdateNameInformation() fonksiyonu receiver olarak p aldığı için FirstName’e ait güncellemeyi de 0003 adresindeki değerde yapar. Dolayısıyla çıktı olarak “Ada” görmemizin sebebi, güncellemeyi kopya olan 0003 adresindeki FirstName için yapıp, ekrana 0001 adresindeki FirstName’in değerini yazdırıyor olmamızdır.
Peki değişikliği fonksiyonun receiver olarak kabul ettiği struct’ta nasıl yapacağız? “Hadi devvvam edelim!”
Pointer Operations
Update işleminin person üzerinde yapılması için kodda bazı değişiklikler yapalım:
Kodu çalıştırdığımızda console ekranında (⌛️ bu sefer daha kısa tutuyorum⏳) “Augusta Lovelace” yazdığını görünce bir taraftan sevinirken bir taraftan da soru işaretleri oluşmaya başladığını görür gibiyim. Bu yüzden ilk olarak 11. satırdaki & ve 18–19. satırlardaki * operatörlerinin ne anlama geldiklerini açıklayayım.
&value→ Değişkenin işaret ettiği değerin bellekteki adresini verir.
*address→ Hafıza adresinin (person address) işaret ettiği değeri verir.
Burada fonksiyon tanımlamasındaki *Person ile fonksiyon içerisinde yer alan *pointerToPerson’ın başındaki * operatörü farklı anlam içermektedir.
*Person → Type description — person pointer’ı ile çalıştığımız anlamına gelir.
*pointerToPerson → Operator — pointer’ın referans verdiği değeri değiştirmek istediğimiz anlamına gelir.
Pointer Shortcut
Go’da yukarıda açıkladığımız kod ile aynı sonucu veren bir kısayol mevcut.
11. satırdaki personPointer’ı person’a çevirdiğimizde, bu kısayol sayesinde receiver olarak person’a ait pointer (*Person) alan bir fonksiyona, Person türünden bir değişken (person) ile erişebiliyoruz. Go bizim için person değişkenini, person pointer’ına dönüştürüyor. 💯
Struct ve pointer kavramlarının ne olduğu, ne zaman kullanılması gerektiğinden bahsetmeye çalıştım. Vakit ayırıp okuduğunuz için teşekkür ederim. ✨
🏃‍♀️ Az kalsın söylemeyi unutuyordum… Yukarıda yazdığımız kodda ismi geçen gerçek adı Augusta Ada Byron olan mucit, tüm dünya tarafından gerçek adıyla değil Ada Lovelace ismiyle bilinmektedir. 🏃‍♀️
Kaynaklar
📚 Go Design Patterns , Mario Castro Contreras
💻 Master the fundamentals and advanced features of the Go Programming Language (Golang) , Stephen Grider