08 Nisan 2025
Büyük, hızlı ve güvenilir yazılımlar oluşturmak genellikle karmaşıklığı yönetmek gibi hissettirir. Peki ya bunu basitleştirmek için sıfırdan tasarlanmış, hızı ve anlaşılır eşzamanlılığı karmaşaya boğulmadan sunan bir dil olsaydı? Karşınızda Go (genellikle Golang olarak da adlandırılır), modern yazılım geliştirmenin zorluklarına, özellikle de büyük ölçekte, doğrudan çözüm bulmak için tasarlanmış bir programlama dili. Basitliği, verimliliği ve eşzamanlı programlamayı önceliklendirerek geliştiricileri son derece üretken kılmayı hedefler. Bu Go eğitimi, Go programlamayı öğrenmek için gereken temel kavramlarda size yol göstererek başlangıç noktanız olarak hizmet eder.
Go, Google bünyesinde 2007 civarında ortaya çıktı. Sistem programlama alanında deneyimli mühendisler tarafından, hayran oldukları dillerin en iyi yönlerini birleştirirken sevmedikleri karmaşıklıkları (özellikle C++'da bulunanları) atmak amacıyla tasarlandı. 2009'da kamuoyuna duyurulan ve 2012'de kararlı 1.0 sürümüne ulaşan Go, yazılım geliştirme topluluğunda hızla ilgi gördü.
Go'yu tanımlayan temel özellikler şunlardır:
gofmt
), bağımlılıkları yönetme (go mod
), test etme (go test
), derleme (go build
) ve daha fazlası için mükemmel komut satırı araçlarıyla birlikte gelir ve geliştirme sürecini kolaylaştırır.Dilin, Renée French tarafından tasarlanan ve Go topluluğunun bir sembolü haline gelen Gopher adında sevimli bir maskotu bile vardır. Resmi adı Go olsa da, “Golang” terimi orijinal web sitesi alan adı (golang.org
) nedeniyle ortaya çıkmıştır ve özellikle çevrimiçi arama yaparken yaygın bir takma ad olarak kalmıştır.
Herhangi bir Go kodu yazmadan önce Go derleyicisine ve araçlarına ihtiyacınız vardır. Resmi Go web sitesi olan go.dev adresini ziyaret edin ve işletim sisteminiz (Windows, macOS, Linux) için basit kurulum talimatlarını izleyin. Yükleyici, go
gibi gerekli komutları ayarlar.
Geleneksel ilk programı oluşturalım. hello.go
adında bir dosya oluşturun ve aşağıdaki kodu yazın veya yapıştırın:
package main
import "fmt"
// Bu, yürütmenin başladığı ana fonksiyondur.
func main() {
// Println, konsola bir satır metin yazdırır.
fmt.Println("Merhaba, Gopher!")
}
Bu basit Go kodu örneğini inceleyelim:
package main
: Her Go programı bir paket bildirimi ile başlar. main
paketi özeldir; bu paketin çalıştırılabilir bir programa derlenmesi gerektiğini belirtir.import "fmt"
: Bu satır, Go'nun standart kütüphanesinin bir parçası olan fmt
paketini içe aktarır. fmt
paketi, konsola metin yazdırmak gibi biçimlendirilmiş girdi ve çıktı (G/Ç) için fonksiyonlar sağlar.func main() { ... }
: Bu, main
fonksiyonunu tanımlar. Çalıştırılabilir bir Go programının yürütülmesi her zaman main
paketinin main
fonksiyonunda başlar.fmt.Println("Merhaba, Gopher!")
: Bu, içe aktarılan fmt
paketinden Println
fonksiyonunu çağırır. Println
(Satır Yazdır), “Merhaba, Gopher!” metin dizesini konsola yazdırır ve ardından bir yeni satır karakteri ekler.Bu programı çalıştırmak için terminalinizi veya komut istemcinizi açın, hello.go
dosyasını kaydettiğiniz dizine gidin ve şu komutu yürütün:
go run hello.go
Konsolunuzda aşağıdaki çıktıyı görmelisiniz:
Merhaba, Gopher!
Tebrikler! İlk Go programınızı çalıştırdınız.
İlk programınız başarıyla çalıştığına göre, Go dilinin temel yapı taşlarını keşfedelim. Bu bölüm, başlangıç seviyesi bir Go eğitimi olarak hizmet vermektedir.
Değişkenler, program yürütülmesi sırasında değişebilecek verileri saklamak için kullanılır. Go'da, değişkenleri kullanmadan önce bildirmeniz gerekir, bu da derleyicinin tip güvenliğini sağlamasına yardımcı olur.
var
Kullanımı: var
anahtar kelimesi, bir veya daha fazla değişken bildirmek için standart yoldur. Tipi, değişken adından sonra açıkça belirtebilirsiniz.
package main
import "fmt"
func main() {
var selamlama string = "Go'ya Hoş Geldiniz!" // Bir string değişkeni bildir
var puan int = 100 // Bir integer değişkeni bildir
var pi float64 = 3.14159 // 64-bit kayan noktalı bir değişken bildir
var aktifMi bool = true // Bir boolean değişkeni bildir
fmt.Println(selamlama)
fmt.Println("Başlangıç Puanı:", puan)
fmt.Println("Pi Yaklaşık:", pi)
fmt.Println("Aktif Durumu:", aktifMi)
}
Kısa Değişken Bildirimi :=
: Fonksiyonlar içinde Go, değişkenleri aynı anda bildirmek ve başlatmak için kısa :=
sözdizimini sunar. Go, değişkenin tipini sağ tarafta atanan değerden otomatik olarak çıkarır.
package main
import "fmt"
func main() {
kullaniciAdi := "Gopher123" // Go, 'kullaniciAdi'nın bir string olduğunu çıkarır
seviye := 5 // Go, 'seviye'nin bir int olduğunu çıkarır
ilerleme := 0.75 // Go, 'ilerleme'nin bir float64 olduğunu çıkarır
fmt.Println("Kullanıcı Adı:", kullaniciAdi)
fmt.Println("Seviye:", seviye)
fmt.Println("İlerleme:", ilerleme)
}
Önemli Not: :=
sözdizimi yalnızca fonksiyonlar içinde kullanılabilir. Paket seviyesinde (herhangi bir fonksiyonun dışında) bildirilen değişkenler için var
anahtar kelimesini kullanmalısınız.
Sıfır Değerler: Eğer bir değişkeni var
kullanarak açık bir başlangıç değeri vermeden bildirirseniz, Go ona otomatik olarak bir sıfır değer atar. Sıfır değer tipe bağlıdır:
0
bool
) için false
string
tipleri için ""
(boş dize)nil
.package main
import "fmt"
func main() {
var sayac int
var mesaj string
var etkin bool
var kullaniciPuani *int // İşaretçi tipi
var gorev func() // Fonksiyon tipi
fmt.Println("Sıfır Int:", sayac) // Çıktı: Sıfır Int: 0
fmt.Println("Sıfır String:", mesaj) // Çıktı: Sıfır String:
fmt.Println("Sıfır Bool:", etkin) // Çıktı: Sıfır Bool: false
fmt.Println("Sıfır İşaretçi:", kullaniciPuani) // Çıktı: Sıfır İşaretçi: <nil>
fmt.Println("Sıfır Fonksiyon:", gorev) // Çıktı: Sıfır Fonksiyon: <nil>
}
Go, birkaç temel yerleşik veri tipi sağlar:
int
, int8
, int16
, int32
, int64
, uint
, uint8
, vb.): Tam sayıları temsil eder. int
ve uint
platforma bağlıdır (genellikle 32 veya 64 bit). Gerektiğinde belirli boyutları kullanın (örn. ikili veri formatları veya performans optimizasyonu için). uint8
, byte
için bir takma addır.float32
, float64
): Ondalık noktalı sayıları temsil eder. float64
varsayılandır ve genellikle daha iyi hassasiyet için tercih edilir.bool
): Doğruluk değerlerini temsil eder, ya true
ya da false
.string
): UTF-8 olarak kodlanmış karakter dizilerini temsil eder. Go'daki dizeler değişmezdir – bir kez oluşturulduktan sonra içerikleri doğrudan değiştirilemez. Dizeleri değiştiriyormuş gibi görünen işlemler aslında yenilerini oluşturur.İşte temel tipleri kullanan bir Go örneği:
package main
import "fmt"
func main() {
urun := "Laptop" // string
miktar := 2 // int
fiyat := 1250.75 // float64 (çıkarıldı)
stoktaVar := true // bool
// Go, farklı sayısal tipler arasında açık tip dönüşümleri gerektirir.
toplamMaliyet := float64(miktar) * fiyat // Çarpma için int 'miktar'ı float64'e dönüştür
fmt.Println("Ürün:", urun)
fmt.Println("Miktar:", miktar)
fmt.Println("Birim Fiyat:", fiyat)
fmt.Println("Stokta Var:", stoktaVar)
fmt.Println("Toplam Maliyet:", toplamMaliyet)
}
Bu örnek, tip çıkarımı kullanarak değişken bildirimini ve farklı sayısal tiplerle aritmetik işlem yaparken açık tip dönüşümü ihtiyacını vurgulamaktadır.
Sabitler, değişkenlere benzer şekilde adları değerlere bağlar, ancak değerleri derleme zamanında sabittir ve program yürütülmesi sırasında değiştirilemez. const
anahtar kelimesi kullanılarak bildirilirler.
package main
import "fmt"
const UygulamaSurumu = "1.0.2" // String sabiti
const MaksBaglanti = 1000 // Tamsayı sabiti
const Pi = 3.14159 // Kayan noktalı sabit
func main() {
fmt.Println("Uygulama Sürümü:", UygulamaSurumu)
fmt.Println("İzin Verilen Maksimum Bağlantı:", MaksBaglanti)
fmt.Println("Pi değeri:", Pi)
}
Go ayrıca, artan tamsayı sabitlerinin tanımını basitleştiren özel iota
anahtar kelimesini sağlar. Genellikle numaralandırılmış tipler (enum'lar) oluşturmak için kullanılır. iota
, bir const
bloğu içinde 0'dan başlar ve o bloktaki her bir sonraki sabit bildirimi için bir artar.
package main
import "fmt"
// int tabanlı özel LogSeviyesi tipi tanımla
type LogSeviyesi int
const (
Debug LogSeviyesi = iota // 0
Info // 1 (iota artar)
Warning // 2
Error // 3
)
func main() {
mevcutSeviye := Info
fmt.Println("Mevcut Log Seviyesi:", mevcutSeviye) // Çıktı: Mevcut Log Seviyesi: 1
fmt.Println("Hata Seviyesi:", Error) // Çıktı: Hata Seviyesi: 3
}
Kontrol akışı ifadeleri, kod ifadelerinin hangi sırada yürütüleceğini belirler.
if / else if / else
: Boolean ifadelerine dayalı olarak kod bloklarını koşullu olarak yürütür. Go'da koşullar etrafında parantez ()
kullanılmaz, ancak küme parantezleri {}
her zaman gereklidir, tek ifadeli bloklar için bile.
package main
import "fmt"
func main() {
sicaklik := 25
if sicaklik > 30 {
fmt.Println("Hava oldukça sıcak.")
} else if sicaklik < 10 {
fmt.Println("Hava oldukça soğuk.")
} else {
fmt.Println("Sıcaklık ılıman.") // Bu yazdırılacak
}
// Koşuldan önce kısa bir ifade gelebilir; orada bildirilen değişkenler
// if/else bloğuyla sınırlıdır.
if limit := 100; sicaklik < limit {
fmt.Printf("Sıcaklık %d, limit %d'nin altında.\n", sicaklik, limit)
} else {
fmt.Printf("Sıcaklık %d, limit %d'nin altında DEĞİL.\n", sicaklik, limit)
}
}
for
: Go'nun yalnızca bir döngü yapısı vardır: çok yönlü for
döngüsü. Diğer dillerden aşina olunan çeşitli şekillerde kullanılabilir:
for
döngüsü (başlatma; koşul; son işlem):
for i := 0; i < 5; i++ {
fmt.Println("İterasyon:", i)
}
while
döngüsü gibi davranır):
toplam := 1
for toplam < 100 { // toplam 100'den küçük olduğu sürece döngü
toplam += toplam
}
fmt.Println("Son toplam:", toplam) // Çıktı: Son toplam: 128
break
veya return
kullanın):
sayac := 0
for {
fmt.Println("Döngüde...")
sayac++
if sayac > 3 {
break // Döngüden çık
}
}
for...range
: Dilimler, diziler, map'ler, dizeler ve kanallar gibi veri yapılarındaki öğeler üzerinde yineler. Her öğe için dizin/anahtar ve değeri sağlar.
renkler := []string{"Kırmızı", "Yeşil", "Mavi"}
// Hem dizini hem de değeri al
for indeks, renk := range renkler {
fmt.Printf("İndeks: %d, Renk: %s\n", indeks, renk)
}
// Yalnızca değere ihtiyacınız varsa, dizini yok saymak için boş tanımlayıcı _ kullanın
fmt.Println("Renkler:")
for _, renk := range renkler {
fmt.Println("- ", renk)
}
// Bir dizedeki karakterler (runelar) üzerinde yinele
for i, r := range "Go!" {
fmt.Printf("İndeks %d, Rune %c\n", i, r)
}
switch
: Uzun if-else if
zincirlerine daha temiz bir alternatif sağlayan çok yönlü bir koşullu ifade. Go'nun switch
'i birçok C benzeri dilden daha esnektir:
break
gerekmez).switch
, bir ifade olmadan kullanılabilir (true
değerini durum ifadeleriyle karşılaştırır).package main
import (
"fmt"
"time"
)
func main() {
gun := time.Now().Weekday()
fmt.Println("Bugün:", gun) // Örnek: Bugün: Tuesday
switch gun {
case time.Saturday, time.Sunday: // Bir durum için birden fazla değer
fmt.Println("Hafta sonu!")
case time.Monday:
fmt.Println("Çalışma haftasının başlangıcı.")
default: // İsteğe bağlı varsayılan durum
fmt.Println("Hafta içi.")
}
// İfadesiz switch, temiz bir if/else if zinciri gibi davranır
saat := time.Now().Hour()
switch { // Örtük olarak 'true' üzerinde geçiş yapılır
case saat < 12:
fmt.Println("Günaydın!")
case saat < 17:
fmt.Println("Tünaydın!")
default:
fmt.Println("İyi akşamlar!")
}
}
Go, birkaç temel veri yapısı için yerleşik destek sağlar.
Go'daki diziler, bildirim anında belirlenen sabit bir boyuta sahiptir. Boyut, dizinin tipinin bir parçasıdır ([3]int
, [4]int
'den farklı bir tiptir).
package main
import "fmt"
func main() {
// 3 tamsayıdan oluşan bir dizi bildir. Sıfır değerleriyle (0'lar) başlatılır.
var sayilar [3]int
sayilar[0] = 10
sayilar[1] = 20
// sayilar[2] 0 olarak kalır (sıfır değer)
fmt.Println("Sayılar:", sayilar) // Çıktı: Sayılar: [10 20 0]
fmt.Println("Uzunluk:", len(sayilar)) // Çıktı: Uzunluk: 3
// Satır içinde bir dizi bildir ve başlat
asallar := [5]int{2, 3, 5, 7, 11}
fmt.Println("Asallar:", asallar) // Çıktı: Asallar: [2 3 5 7 11]
// Derleyicinin elemanları ... kullanarak saymasına izin ver
sesliler := [...]string{"a", "e", "i", "o", "u"}
fmt.Println("Sesliler:", sesliler, "Uzunluk:", len(sesliler)) // Çıktı: Sesliler: [a e i o u] Uzunluk: 5
}
Dizilerin kullanım alanları olsa da (örneğin, boyut gerçekten sabit ve biliniyorsa), esneklikleri nedeniyle Go'da dilimler çok daha yaygın olarak kullanılır.
Dilimler, Go'da diziler için kullanılan temel veri yapısıdır. Dizilere göre daha güçlü, esnek ve kullanışlı bir arayüz sağlarlar. Dilimler dinamik boyutludur ve temel alınan dizilere değiştirilebilir görünümler sunar.
package main
import "fmt"
func main() {
// make(tip, uzunluk, kapasite) kullanarak bir string dilimi oluştur
// Kapasite isteğe bağlıdır; belirtilmezse, uzunluğa varsayılan olarak ayarlanır.
// Uzunluk: dilimin şu anda içerdiği eleman sayısı.
// Kapasite: temel alınan dizideki eleman sayısı (dilimin ilk elemanından başlayarak).
isimler := make([]string, 2, 5) // Uzunluk 2, Kapasite 5
isimler[0] = "Alice"
isimler[1] = "Bob"
fmt.Println("Başlangıç İsimleri:", isimler, "Len:", len(isimler), "Cap:", cap(isimler)) // Çıktı: Başlangıç İsimleri: [Alice Bob] Len: 2 Cap: 5
// Append, sona eleman ekler. Uzunluk kapasiteyi aşarsa,
// yeni, daha büyük bir temel dizi ayrılır ve dilim ona işaret eder.
isimler = append(isimler, "Charlie")
isimler = append(isimler, "David", "Eve") // Birden fazla eleman eklenebilir
fmt.Println("Eklenen İsimler:", isimler, "Len:", len(isimler), "Cap:", cap(isimler)) // Çıktı: Eklenen İsimler: [Alice Bob Charlie David Eve] Len: 5 Cap: 5 (veya yeniden tahsis edilirse muhtemelen daha büyük)
// Dilim değişmezi (bir dilim ve temel bir dizi oluşturur)
puanlar := []int{95, 88, 72, 100}
fmt.Println("Puanlar:", puanlar) // Çıktı: Puanlar: [95 88 72 100]
// Bir dilimi dilimlemek: *aynı* temel diziye başvuran yeni bir dilim başlığı oluşturur.
// dilim[düşük:yüksek] - düşük indeksteki elemanı içerir, yüksek indeksteki elemanı hariç tutar.
enYuksekPuanlar := puanlar[1:3] // İndeks 1 ve 2'deki elemanlar (değer: 88, 72)
fmt.Println("En Yüksek Puanlar:", enYuksekPuanlar) // Çıktı: En Yüksek Puanlar: [88 72]
// Alt dilimi değiştirmek orijinal dilimi (ve temel diziyi) etkiler
enYuksekPuanlar[0] = 90
fmt.Println("Değiştirilmiş Puanlar:", puanlar) // Çıktı: Değiştirilmiş Puanlar: [95 90 72 100]
// Düşük sınırı atlamak varsayılan olarak 0'dır, yüksek sınırı atlamak varsayılan olarak uzunluktur
ilkIki := puanlar[:2]
sonIki := puanlar[2:]
fmt.Println("İlk İki:", ilkIki) // Çıktı: İlk İki: [95 90]
fmt.Println("Son İki:", sonIki) // Çıktı: Son İki: [72 100]
}
Temel dilim işlemleri arasında len()
(mevcut uzunluk), cap()
(mevcut kapasite), append()
(eleman ekleme) ve [düşük:yüksek]
sözdizimini kullanarak dilimleme bulunur.
Map'ler (Haritalar), Go'nun yerleşik hash tabloları veya sözlük uygulamasıdır. Sırasız anahtar-değer çiftleri koleksiyonlarını saklarlar; burada tüm anahtarlar aynı türde ve tüm değerler aynı türde olmalıdır.
package main
import "fmt"
func main() {
// make kullanarak string anahtarları ve int değerleri olan boş bir map oluştur
yaslar := make(map[string]int)
// Anahtar-değer çiftlerini ayarla
yaslar["Alice"] = 30
yaslar["Bob"] = 25
yaslar["Charlie"] = 35
fmt.Println("Yaşlar map'i:", yaslar) // Çıktı: Yaşlar map'i: map[Alice:30 Bob:25 Charlie:35] (sıra garanti edilmez)
// Anahtarı kullanarak bir değer al
aliceYasi := yaslar["Alice"]
fmt.Println("Alice'in Yaşı:", aliceYasi) // Çıktı: Alice'in Yaşı: 30
// Var olmayan bir anahtar için değer almak, değer türü için sıfır değerini döndürür (int için 0)
davidYasi := yaslar["David"]
fmt.Println("David'in Yaşı:", davidYasi) // Çıktı: David'in Yaşı: 0
// Bir anahtar-değer çiftini sil
delete(yaslar, "Bob")
fmt.Println("Bob Silindikten Sonra:", yaslar) // Çıktı: Bob Silindikten Sonra: map[Alice:30 Charlie:35]
// İki değerli atama formunu kullanarak bir anahtarın var olup olmadığını kontrol et
// Bir map anahtarına erişirken, isteğe bağlı olarak ikinci bir boolean değeri alabilirsiniz:
// 1. Değer (veya anahtar yoksa sıfır değeri)
// 2. Bir boolean: anahtar mevcutsa true, aksi takdirde false
val, mevcut := yaslar["Bob"] // Değere ihtiyaç yoksa boş tanımlayıcı _ kullanın (örn. _, mevcut := ...)
fmt.Printf("Bob mevcut mu? %t, Değer: %d\n", mevcut, val) // Çıktı: Bob mevcut mu? false, Değer: 0
charlieYasi, charlieMevcut := yaslar["Charlie"]
fmt.Printf("Charlie mevcut mu? %t, Yaş: %d\n", charlieMevcut, charlieYasi) // Çıktı: Charlie mevcut mu? true, Yaş: 35
// Bir map bildirmek ve başlatmak için map değişmezi
baskentler := map[string]string{
"France": "Paris",
"Japan": "Tokyo",
"USA": "Washington D.C.",
}
fmt.Println("Başkentler:", baskentler)
}
Fonksiyonlar, kodu yeniden kullanılabilir birimler halinde organize etmek için temel yapı taşlarıdır. func
anahtar kelimesi kullanılarak bildirilirler.
package main
import (
"fmt"
"errors" // Hata değerleri oluşturmak için standart kütüphane paketi
)
// İki int parametresi alan ve bunların int toplamını döndüren basit bir fonksiyon.
// Parametre tipleri adı takip eder: func fonksiyonAdi(param1 tip1, param2 tip2) donusTipi { ... }
func topla(x int, y int) int {
return x + y
}
// Ardışık parametreler aynı tipe sahipse, tipi
// sonuncusu hariç hepsinden çıkarabilirsiniz.
func carp(x, y int) int {
return x * y
}
// Go fonksiyonları birden fazla değer döndürebilir. Bu, bir sonucu
// ve bir hata durumunu aynı anda döndürmek için deyimsel bir yoldur.
func bol(pay float64, payda float64) (float64, error) {
if payda == 0 {
// Payda sıfırsa yeni bir hata değeri oluştur ve döndür
return 0, errors.New("sıfıra bölmeye izin verilmez")
}
// Başarılı olursa hesaplanan sonucu ve hata için 'nil' döndür
// 'nil', hata tipleri (ve işaretçiler, dilimler, map'ler gibi diğerleri) için sıfır değeridir.
return pay / payda, nil
}
func main() {
toplam := topla(15, 7)
fmt.Println("Toplam:", toplam) // Çıktı: Toplam: 22
carpim := carp(6, 7)
fmt.Println("Çarpım:", carpim) // Çıktı: Çarpım: 42
// Birden fazla değer döndüren fonksiyonu çağır
sonuc, err := bol(10.0, 2.0)
// Hata değerini hemen kontrol et
if err != nil {
fmt.Println("Hata:", err)
} else {
fmt.Println("Bölme Sonucu:", sonuc) // Çıktı: Bölme Sonucu: 5
}
// Geçersiz girdiyle tekrar çağır
sonuc2, err2 := bol(5.0, 0.0)
if err2 != nil {
fmt.Println("Hata:", err2) // Çıktı: Hata: sıfıra bölmeye izin verilmez
} else {
fmt.Println("Bölme Sonucu 2:", sonuc2)
}
}
Go fonksiyonlarının birden fazla değer döndürme yeteneği, açık hata yönetimi mekanizması için çok önemlidir.
Go kodu paketler halinde organize edilir. Bir paket, birlikte derlenen tek bir dizinde bulunan kaynak dosyaları (.go
dosyaları) koleksiyonudur. Paketler kodun yeniden kullanımını ve modülerliği teşvik eder.
package paketAdi
bildirimi ile başlamalıdır. Aynı dizindeki dosyalar aynı pakete ait olmalıdır. main
paketi özeldir, çalıştırılabilir bir programı belirtir.import
anahtar kelimesini kullanın. Standart kütüphane paketleri kısa adlarıyla içe aktarılır (örn. "fmt"
, "math"
, "os"
). Harici paketler genellikle kaynak depo URL'lerine dayalı bir yol kullanır (örn. "github.com/gin-gonic/gin"
).
import (
"fmt" // Standart kütüphane
"math/rand" // math'ın alt paketi
"os"
// disPaketim "github.com/kullanici/haricipaket" // İçe aktarmalara takma ad verilebilir
)
go.mod
dosyasıyla tanımlanır. Temel komutlar şunları içerir:
go mod init <modül_yolu>
: Yeni bir modül başlatır (go.mod
oluşturur).go get <paket_yolu>
: Bir bağımlılığı ekler veya günceller.go mod tidy
: Kullanılmayan bağımlılıkları kaldırır ve kod içe aktarmalarına dayalı olarak eksik olanları ekler.Eşzamanlılık, aynı anda çalışıyormuş gibi görünen birden fazla görevi yönetmeyi içerir. Go, İletişim Kuran Sıralı Süreçler'den (CSP) esinlenerek eşzamanlılık için güçlü ancak basit yerleşik özelliklere sahiptir.
Goroutine'ler: Bir goroutine, Go çalışma zamanı tarafından başlatılan ve yönetilen bağımsız olarak yürütülen bir fonksiyondur. Bunu son derece hafif bir iş parçacığı olarak düşünün. Bir fonksiyon veya metot çağrısının başına go
anahtar kelimesini ekleyerek basitçe bir goroutine başlatırsınız.
Kanallar: Kanallar, goroutine'ler arasında değer gönderip almanızı sağlayan, iletişim ve senkronizasyonu mümkün kılan tipli iletkenlerdir.
ch := make(chan Tip)
(örn. make(chan string)
)ch <- deger
degisken := <-ch
(bu, bir değer gönderilene kadar engellenir)İşte goroutine'leri ve kanalları gösteren çok temel bir Go örneği:
package main
import (
"fmt"
"time"
)
// Bu fonksiyon bir goroutine olarak çalışacak.
// Bir mesaj ve mesajı geri göndermek için bir kanal alır.
func mesajGoster(msg string, messages chan string) {
fmt.Println("Goroutine çalışıyor...")
time.Sleep(1 * time.Second) // Biraz iş simüle et
messages <- msg // Mesajı kanala gönder
fmt.Println("Goroutine bitti.")
}
func main() {
// String değerleri taşıyan bir kanal oluştur.
// Bu, arabelleksiz bir kanaldır, yani gönderme/alma işlemleri
// diğer taraf hazır olana kadar engellenir.
mesajKanali := make(chan string)
// mesajGoster fonksiyonunu bir goroutine olarak başlat
// 'go' anahtar kelimesi bu çağrıyı engellemeyen yapar; main hemen devam eder.
go mesajGoster("Ping!", mesajKanali)
fmt.Println("Ana fonksiyon mesaj bekliyor...")
// Kanaldan mesajı al.
// Bu işlem, goroutine tarafından mesajKanali'na bir mesaj gönderilene
// kadar ana fonksiyonu ENGELLER.
alinanMsg := <-mesajKanali
fmt.Println("Ana fonksiyon aldı:", alinanMsg) // Çıktı (~1 saniye sonra): Ana fonksiyon aldı: Ping!
// Goroutine'in son yazdırma ifadesinin main çıkmadan önce görünmesine izin ver
time.Sleep(50 * time.Millisecond)
}
Bu basit örnek, eşzamanlı bir görevi başlatmayı ve sonucunu bir kanal aracılığıyla güvenli bir şekilde almayı göstermektedir. Go'nun eşzamanlılık modeli, arabellekli kanalları, birden fazla kanalı işlemek için güçlü select
ifadesini ve sync
paketindeki senkronizasyon ilkellerini içeren derin bir konudur.
Go, istisnaları kullanan dillere kıyasla hata yönetimine farklı bir yaklaşım benimser. Hatalar normal değerler olarak ele alınır. Potansiyel olarak başarısız olabilecek fonksiyonlar tipik olarak son dönüş değeri olarak bir error
arayüz tipi döndürür.
error
arayüzünün tek bir metodu vardır: Error() string
.nil
bir hata değeri başarıyı gösterir.nil
olmayan bir hata değeri başarısızlığı gösterir ve değerin kendisi genellikle hatayla ilgili ayrıntıları içerir.package main
import (
"fmt"
"os"
)
func main() {
// Muhtemelen var olmayan bir dosyayı açmaya çalış
dosya, err := os.Open("kesinlikle_var_olmayan_bir_dosya.txt")
// Deyimsel hata kontrolü: err'nin nil olup olmadığını kontrol et
if err != nil {
fmt.Println("KRİTİK: Dosya açılırken hata:", err)
// Hatayı uygun şekilde ele al. Burada sadece çıkıyoruz.
// Gerçek uygulamalarda, hatayı günlüğe kaydedebilir,
// mevcut fonksiyondan döndürebilir veya bir geri dönüş deneyebilirsiniz.
return // Ana fonksiyondan çık
}
// Eğer err nil ise, fonksiyon başarılı olmuştur.
// Artık 'dosya' değişkenini güvenle kullanabiliriz.
fmt.Println("Dosya başarıyla açıldı!") // Bu hata senaryosunda yazdırılmayacak
// Dosyalar gibi kaynakları kapatmak çok önemlidir.
// 'defer', bir fonksiyon çağrısını (dosya.Close()) çevreleyen
// fonksiyon (main) dönmeden hemen önce çalışacak şekilde zamanlar.
defer dosya.Close()
// ... dosyadan okuma veya dosyaya yazma işlemlerine devam et ...
fmt.Println("Dosya üzerinde işlemler gerçekleştiriliyor...")
}
Bu açık if err != nil
kontrolü, kontrol akışını çok net hale getirir ve geliştiricileri potansiyel başarısızlıkları aktif olarak düşünmeye ve ele almaya teşvik eder. defer
ifadesi, kaynakların güvenilir bir şekilde temizlenmesini sağlamak için genellikle hata kontrolleriyle birlikte kullanılır.
Go'nun önemli bir gücü, standart dağıtımla birlikte gelen mükemmel, uyumlu araç takımıdır:
go run <dosyaadi.go>
: Tek bir Go kaynak dosyasını veya bir main paketini doğrudan derler ve çalıştırır. Hızlı testler için kullanışlıdır.go build
: Go paketlerini ve bağımlılıklarını derler. Varsayılan olarak, paket main
ise çalıştırılabilir bir dosya oluşturur.gofmt
: Go kaynak kodunu resmi Go stil yönergelerine göre otomatik olarak biçimlendirir. Projeler ve geliştiriciler arasında tutarlılık sağlar. Mevcut dizindeki ve alt dizinlerdeki tüm Go dosyalarını biçimlendirmek için gofmt -w .
kullanın.go test
: Birim testlerini ve kıyaslamaları çalıştırır. Testler _test.go
dosyalarında bulunur.go mod
: Bağımlılıkları yönetmek için Go modülleri aracı (örn. go mod init
, go mod tidy
, go mod download
).go get <paket_yolu>
: Mevcut modülünüze yeni bağımlılıklar ekler veya mevcut olanları günceller.go vet
: Go kaynak kodunu şüpheli yapılar ve derleyicinin yakalayamayabileceği potansiyel hatalar açısından kontrol eden statik bir analiz aracıdır.go doc <paket> [sembol]
: Paketler veya belirli semboller için belgeleri görüntüler.Bu entegre araç takımı, derleme, test etme, biçimlendirme ve bağımlılık yönetimi gibi yaygın geliştirme görevlerini önemli ölçüde basitleştirir.
Go, modern yazılım geliştirme için çekici bir teklif sunar: basitliği, performansı ve güçlü özellikleri, özellikle eşzamanlı sistemler, ağ hizmetleri ve büyük ölçekli uygulamalar oluşturmak için dengeleyen bir dil. Temiz sözdizimi, güçlü statik tipleme, çöp toplama yoluyla otomatik bellek yönetimi, yerleşik eşzamanlılık ilkelleri, kapsamlı standart kütüphane ve mükemmel araç takımı; daha hızlı geliştirme döngülerine, daha kolay bakıma ve daha güvenilir yazılımlara katkıda bulunur. Bu, onu yalnızca yeni yeşil alan projeleri için değil, aynı zamanda performans, eşzamanlılık ve sürdürülebilirliğin temel hedefler olduğu mevcut sistemleri modernize etmek veya kod taşımak için de güçlü bir seçenek haline getirir.