go
Go dersleri
Go/Temeller

Fonksiyonlar

Çoklu dönüş değerleri, variadic fonksiyonlar, closure'lar ve defer.

Ders 4 / 2125 dkBaşlangıç
Bu derste öğreneceklerin
  • Fonksiyon tanımlama ve parametreler
  • Çoklu ve isimlendirilmiş dönüş değerleri
  • Variadic fonksiyonlar
  • Birinci sınıf fonksiyonlar ve fonksiyon tipleri
  • Anonim fonksiyonlar ve closure'lar
  • defer: LIFO sırası ve argümanların değerlendirilmesi
  • Değer ile geçirme (pass by value)

Fonksiyonlar, bir programda tekrar eden işi tek bir isim altında toplamanın yoludur. Ama iyi bir fonksiyon yalnızca kod tekrarını azaltmaz; bir sözleşme tanımlar: Şunu verirsen, bunu alırsın. Bu sözleşme ne kadar net olursa, kodun geri kalanı o kadar az şey bilmek zorunda kalır. Go'nun fonksiyon tasarımı baştan sona bu netlik fikrine dayanır.

Go'da fonksiyonlar birinci sınıf değerlerdir (first-class values): Bir değişkende saklanabilir, başka bir fonksiyona argüman olarak geçirilebilir, bir fonksiyondan döndürülebilir ve bir dilimin ya da haritanın içine konabilir. Bu, tek başına, Go'da olmayan pek çok nesne yönelimli kalıbın yerini tutar. Buna ek olarak Go, çok az dilde bulunan iki özelliği doğal karşılar: birden fazla değer döndürme ve defer ile temizlik. Hata yönetiminin error dönüşüne dayanması ve kaynakların güvenle serbest bırakılması bu ikisi sayesinde mümkündür.

Bu derste fonksiyon tanımlamayı, çoklu ve isimlendirilmiş dönüş değerlerini, variadic (değişken sayıda argüman alan) fonksiyonları, fonksiyon tiplerini, anonim fonksiyonları ve closure'ları, defer'in kurallarını ve Go'nun her şeyi değer ile geçirdiği gerçeğini öğreneceksin. Bunlar, ilerideki her derste kullanacağın temel yapı taşlarıdır.

Tanımlama ve parametreler

Bir fonksiyon func anahtar kelimesi, ad, parametre listesi, dönüş tipi ve gövdeden oluşur:

main.go
package main

import "fmt"

// Tek parametre, tek dönüş
func square(n int) int {
	return n * n
}

// Aynı tipteki parametreler tek seferde yazılabilir
func rectangleArea(width, height float64) float64 {
	return width * height
}

// Dönüş değeri olmayan fonksiyon
func greet(name string) {
	fmt.Printf("Merhaba %s!\n", name)
}

func main() {
	fmt.Println(square(7))
	fmt.Println(rectangleArea(3, 4.5))
	greet("Deniz")
}
Çıktı
49
13.5
Merhaba Deniz!

Parametre listesinde width, height float64 yazımı, ikisinin de float64 olduğu anlamına gelir. Farklı tipler varsa her birini ayrı yazarsın: func send(msg string, count int).

Fonksiyon adının büyük harfle başlaması onu paket dışına açar. Küçük harfle başlayan fonksiyonlar yalnızca kendi paketlerinde görünür. Bu yüzden yardımcı fonksiyonlarını küçük harfle, API'nin parçası olanları büyük harfle adlandırırsın.

Go'da fonksiyonlar aşırı yüklenemez (overloading yoktur): Aynı isimde iki fonksiyon tanımlayamazsın, parametre listeleri farklı olsa bile. Bunun yerine farklı isimler kullanılır (Parse, ParseInt, ParseFloat) veya jenerikler devreye girer. Varsayılan parametre değeri de yoktur; bu ihtiyaç genelde "seçenek struct'ı" veya "fonksiyonel seçenek" desenleriyle karşılanır.

Çoklu dönüş değerleri

Go'da bir fonksiyon birden fazla değer döndürebilir ve bu, dilin en belirleyici özelliklerinden biridir. En yaygın kullanımı (sonuç, error) çiftidir:

main.go
package main

import (
	"errors"
	"fmt"
)

func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("sıfıra bölme")
	}
	return a / b, nil
}

// İki değeri yer değiştirmek için geçici değişken gerekmez
func swap(a, b string) (string, string) {
	return b, a
}

func main() {
	if result, err := divide(10, 4); err == nil {
		fmt.Println("sonuç:", result)
	}

	if _, err := divide(3, 0); err != nil {
		fmt.Println("hata:", err)
	}

	left, right := swap("sol", "sağ")
	fmt.Println(left, right)
}
Çıktı
sonuç: 2.5
hata: sıfıra bölme
sağ sol

Dönüş değerlerinden ilgilenmediklerini _ (boş tanımlayıcı) ile atarsın. Go, kullanılmayan değişkenlere izin vermediği için bu gereklidir.

İsimlendirilmiş dönüş değerleri

Dönüş değerlerine isim verebilirsin. Bu isimler fonksiyonun başında sıfır değerleriyle tanımlanmış birer yerel değişken gibi davranır ve return ifadesi boş bırakılabilir (buna "çıplak return" denir):

main.go
package main

import (
	"fmt"
	"strings"
)

// İsimlendirilmiş dönüşler belgeleme değeri taşır:
// hangi sayının ne olduğu imzadan anlaşılır.
func analyze(text string) (words, letters int) {
	for _, field := range strings.Fields(text) {
		words++
		letters += len([]rune(field))
	}
	return // words ve letters döndürülür
}

// defer ile birlikte kullanıldığında dönüş değeri değiştirilebilir
func safeDivide(a, b int) (result int, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("kurtarıldı: %v", r)
		}
	}()
	result = a / b // b == 0 ise panik
	return result, nil
}

func main() {
	w, l := analyze("Go öğrenmek çok keyifli")
	fmt.Printf("%d kelime, %d harf\n", w, l)

	fmt.Println(safeDivide(10, 2))
	fmt.Println(safeDivide(1, 0))
}
Çıktı
4 kelime, 20 harf
5 <nil>
0 kurtarıldı: runtime error: integer divide by zero

İsimlendirilmiş dönüşler iki yerde gerçekten değerlidir: imzayı belgelemek için (func Split(s string) (before, after string)) ve defer içinden dönüş değerini değiştirmek için. Uzun fonksiyonlarda çıplak return kullanmak ise okunabilirliği düşürür — hangi değerlerin döndüğünü görmek için fonksiyonun başına bakman gerekir. Kısa fonksiyonlarda sorun değildir; uzunlarda değerleri açıkça yaz.

Variadic fonksiyonlar

Son parametrenin tipinden önce ... yazarsan, fonksiyon o tipten istediğin kadar argüman alabilir. Fonksiyonun içinde bu parametre bir dilimdir:

main.go
package main

import "fmt"

func sum(nums ...int) int {
	total := 0
	for _, n := range nums {
		total += n
	}
	return total
}

// Variadic parametre en sonda olmalı; öncesinde normal parametreler olabilir
func logf(level string, args ...any) string {
	return fmt.Sprintf("[%s] %v", level, args)
}

func main() {
	fmt.Println(sum())           // boş dilim
	fmt.Println(sum(3))          // tek eleman
	fmt.Println(sum(1, 2, 3, 4)) // dört eleman

	// Var olan bir dilimi yaymak için ... son eke gelir
	values := []int{5, 10, 15}
	fmt.Println(sum(values...))

	fmt.Println(logf("bilgi", "kullanıcı", 42, true))
}
Çıktı
0
3
10
30
[bilgi] [kullanıcı 42 true]

fmt.Println ve fmt.Printf'in istediğin kadar argüman alabilmesinin sebebi tam olarak budur: imzaları func Println(a ...any) (n int, err error) biçimindedir.

Fonksiyonlar birer değerdir

Bir fonksiyonun tipi, imzasından oluşur: func(int, int) int. Bu tipteki bir değeri değişkende tutabilir, parametre olarak geçebilir, döndürebilirsin:

main.go
package main

import (
	"fmt"
	"slices"
	"strings"
)

// Fonksiyon tipine isim vermek imzayı okunur kılar
type Transform func(string) string

func applyAll(words []string, fns ...Transform) []string {
	out := make([]string, 0, len(words))
	for _, w := range words {
		for _, fn := range fns {
			w = fn(w)
		}
		out = append(out, w)
	}
	return out
}

func main() {
	// Fonksiyon bir değişkende
	var upper Transform = strings.ToUpper
	fmt.Println(upper("merhaba"))

	// Fonksiyonları parametre olarak geçirme
	words := []string{"  go  ", " dilim ", " harita "}
	result := applyAll(words, strings.TrimSpace, strings.ToUpper)
	fmt.Println(result)

	// Standart kütüphane de fonksiyon alır: sıralama ölçütü
	nums := []int{5, 2, 9, 1}
	slices.SortFunc(nums, func(a, b int) int { return b - a }) // azalan
	fmt.Println(nums)
}
Çıktı
MERHABA
[GO DILIM HARITA]
[9 5 2 1]

Bir fonksiyon tipinin sıfır değeri nil'dir. nil bir fonksiyonu çağırmak panik oluşturur, bu yüzden dışarıdan alınan geri çağırım (callback) alanlarını kullanmadan önce kontrol etmek iyi bir alışkanlıktır.

Anonim fonksiyonlar ve closure'lar

Adı olmayan fonksiyonlar doğrudan yazıldıkları yerde tanımlanır. Asıl güçleri, tanımlandıkları kapsamdaki değişkenleri yakalamalarıdır. Değişkenleri yakalayan bir fonksiyona closure (kapanış) denir:

main.go
package main

import "fmt"

// counter, çağrıldıkça artan bir fonksiyon döndürür.
// n değişkeni dönen fonksiyonun içinde yaşamaya devam eder.
func counter(start int) func() int {
	n := start
	return func() int {
		n++
		return n
	}
}

func main() {
	// Hemen çağrılan anonim fonksiyon
	func(msg string) {
		fmt.Println("anonim:", msg)
	}("merhaba")

	next := counter(10)
	fmt.Println(next(), next(), next())

	// Her çağrı kendi bağımsız durumunu yaratır
	other := counter(100)
	fmt.Println(other(), next())

	// Döngü değişkeni Go 1.22'den beri her yinelemede yenidir:
	// aşağıdaki üç closure üç farklı i değerini yakalar.
	var fns []func() int
	for i := range 3 {
		fns = append(fns, func() int { return i * i })
	}
	for _, f := range fns {
		fmt.Print(f(), " ")
	}
	fmt.Println()
}
Çıktı
anonim: merhaba
11 12 13
101 14
0 1 4

Closure'lar yakaladıkları değişkenin kopyasını değil kendisini kullanır. counter örneğinde n, fonksiyon döndükten sonra bile yaşamaya devam eder; derleyici bunu fark eder ve n'i stack yerine heap'te tutar. Buna "kaçış analizi" (escape analysis) denir ve İşaretçiler dersinde ayrıntısını göreceksin.

defer: temizliği unutmama garantisi

defer ile işaretlenen çağrı, çevreleyen fonksiyon dönerken çalıştırılır — normal dönüşte de, panik sırasında da. Böylece açtığın kaynağı kapatma kodunu, açma kodunun hemen yanına yazarsın:

f, err := os.Open("veri.txt")
if err != nil {
	return err
}
defer f.Close() // ne olursa olsun kapanacak
// ... dosyayla çalış

Üç kuralı bilmen gerekir:

1. Sıra LIFO'dur. Son ertelenen ilk çalışır. Bu, kaynakları ters sırada kapatmak için doğaldır.

2. Argümanlar defer satırında değerlendirilir. Fonksiyon çalıştırılmaz ama argümanları hemen hesaplanır ve saklanır.

3. Ertelenen fonksiyon, isimlendirilmiş dönüş değerlerini değiştirebilir.

main.go
package main

import "fmt"

func deferOrder() {
	for i := range 3 {
		defer fmt.Print(i, " ") // ters sırada çalışır
	}
	fmt.Print("gövde bitti → ")
}

func argEvaluation() {
	x := 1
	defer fmt.Println("ertelenmiş, x =", x) // x'in ŞU ANKİ değeri: 1
	x = 99
	fmt.Println("gövde sonu,   x =", x)
}

func namedReturn() (result int) {
	defer func() { result *= 2 }() // dönüş değerini değiştirir
	return 21
}

func main() {
	deferOrder()
	fmt.Println()
	argEvaluation()
	fmt.Println("namedReturn:", namedReturn())
}
Çıktı
gövde bitti → 2 1 0
gövde sonu,   x = 99
ertelenmiş, x = 1
namedReturn: 42

İkinci kural en çok şaşırtandır: defer fmt.Println(x) satırı x'in o andaki değerini dondurur. Değişkenin son değerini kullanmak istiyorsan ertelemeyi bir anonim fonksiyonun içine al: defer func() { fmt.Println(x) }().

defer'in küçük bir maliyeti vardır ama Go 1.14'ten beri çoğu durumda neredeyse sıfıra yakındır. Milyonlarca kez dönen bir döngünün içinde defer kullanmaktan kaçın: Ertelenen çağrılar fonksiyon bitene kadar birikir. Döngü gövdesini ayrı bir fonksiyona taşımak bu sorunu çözer.

Her şey değer ile geçirilir

Go'da parametreler her zaman kopyalanır. Referans ile geçirme (pass by reference) diye bir şey yoktur. Bir fonksiyonun çağıranın değişkenini değiştirmesini istiyorsan, adresini geçirmelisin:

main.go
package main

import "fmt"

type User struct {
	Name  string
	Score int
}

func rename(u User, name string) { // kopya alır
	u.Name = name
}

func renamePtr(u *User, name string) { // adresi alır
	u.Name = name
}

func appendItem(s []int) {
	s = append(s, 99) // yerel dilim başlığı değişir, çağıranınki değişmez
}

func modifyItem(s []int) {
	if len(s) > 0 {
		s[0] = 42 // altta yatan diziyi değiştirir: çağıran da görür
	}
}

func main() {
	u := User{Name: "Ali", Score: 10}
	rename(u, "Veli")
	fmt.Println("değer ile:", u.Name)
	renamePtr(&u, "Veli")
	fmt.Println("işaretçi ile:", u.Name)

	nums := []int{1, 2, 3}
	appendItem(nums)
	fmt.Println("append sonrası:", nums)
	modifyItem(nums)
	fmt.Println("eleman yazma sonrası:", nums)
}
Çıktı
değer ile: Ali
işaretçi ile: Veli
append sonrası: [1 2 3]
eleman yazma sonrası: [42 2 3]

Dilim örneği ince bir noktadır: Dilim başlığı (işaretçi, uzunluk, kapasite) kopyalanır, ama işaretçi aynı diziyi gösterir. Bu yüzden var olan bir elemanı değiştirmek çağıranı etkiler, append ise etkilemez. Bu ayrımı Diziler ve Dilimler dersinde ayrıntılı ele alıyoruz.

İyi fonksiyon tasarımı

Dilin kuralları bir yana, günlük hayatta asıl fark yaratan şey fonksiyonlarını nasıl tasarladığındır. Go topluluğunda zamanla yerleşmiş birkaç ilke vardır ve bunlar neredeyse her kod incelemesinde karşına çıkar.

Tek bir iş yap. Bir fonksiyonun adını söylerken "ve" kelimesine ihtiyaç duyuyorsan, muhtemelen iki fonksiyon vardır. Adında "işle", "yönet", "hallet" gibi belirsiz kelimeler geçen fonksiyonlar genelde zamanla şişer ve kimsenin dokunmak istemediği bölgelere dönüşür.

Girdiyi daralt, çıktıyı genişlet. Fonksiyonun ihtiyacı olan en küçük bilgiyi parametre olarak al. Koca bir yapılandırma nesnesi yerine yalnızca kullandığın iki alanı almak, fonksiyonu hem test edilebilir hem de yeniden kullanılabilir kılar. Aynı mantıkla, geriye dönerken çağıranın işine yarayacak bilgiyi sakla.

Yan etkileri görünür kıl. Bir fonksiyon kendisine verilen veriyi değiştiriyorsa bunu adında belli etmelidir. Yeni bir sonuç üreten fonksiyonlarla, verilen veriyi yerinde değiştiren fonksiyonları aynı pakette karıştırmak, kullananı sürekli belgelere bakmak zorunda bırakır.

Kısa tut, ama zorlama. Go'da fonksiyon uzunluğu için katı bir kural yoktur. Ekrana sığan fonksiyonlar okumayı kolaylaştırır; ancak bir işi anlamsız biçimde beşe bölmek de okumayı zorlaştırır. Ölçüt satır sayısı değil, fonksiyonun tek bir fikri anlatıp anlatmadığıdır.

Hata yolunu kısa devre yap. Hatalı durumu hemen döndürmek, başarılı yolun girintisiz kalmasını sağlar. Bu sayede bir fonksiyonu okurken, en soldaki sütunu takip ederek asıl işin ne olduğunu görebilirsin.

Son olarak, bir fonksiyonu yazmadan önce onu nasıl çağıracağını düşün. Çağrı satırı okunaklı değilse imza yanlıştır. Go'da belgeleme kültürü de bu fikre dayanır: Bir fonksiyonun belgesi, o fonksiyonun ne yaptığını değil, çağıranın ne bilmesi gerektiğini anlatır.

Sık yapılan hatalar

  • Çıplak return'ü uzun fonksiyonlarda kullanmak. Ne döndüğünü görmek için fonksiyonun başına bakmak gerekir; değerleri açıkça yaz.
  • defer'i döngü içinde kullanmak. Ertelenen çağrılar fonksiyon bitene kadar birikir; gövdeyi ayrı bir fonksiyona taşı.
  • defer argümanlarının hemen değerlendirildiğini unutmak. Son değeri istiyorsan anonim fonksiyon kullan.
  • Hata dönüşünü _ ile yutmak. En azından bir yorumla gerekçesini yaz.
  • Variadic dilimin kopya sanmak. f(s...) çağrısı aynı diziyi paylaşır.
  • nil fonksiyon değerini çağırmak. Geri çağırım alanlarını kullanmadan önce kontrol et.
  • Değer alıcılı fonksiyondan çağıranın struct'ını değiştirmeyi beklemek. Kopya üzerinde çalışırsın; *T kullan.

Alıştırmalar

Alıştırma·En büyük ve en küçük
Kolay

Değişken sayıda tamsayı alıp en küçük ve en büyük değeri döndüren bir fonksiyon yaz. Hiç argüman verilmezse üçüncü dönüş değeri false olsun. Birkaç çağrıyla test et.

İpucu

Variadic parametre fonksiyonun içinde bir dilimdir; len(nums) == 0 kontrolünü ilk satırda yap.

Çözümü göster
main.go
package main

import "fmt"

func minMax(nums ...int) (lo, hi int, ok bool) {
	if len(nums) == 0 {
		return 0, 0, false
	}
	lo, hi = nums[0], nums[0]
	for _, n := range nums[1:] {
		lo = min(lo, n)
		hi = max(hi, n)
	}
	return lo, hi, true
}

func main() {
	fmt.Println(minMax(4, -2, 17, 0))
	fmt.Println(minMax(9))
	fmt.Println(minMax())

	values := []int{3, 8, 1}
	fmt.Println(minMax(values...))
}
Çıktı
-2 17 true
9 9 true
0 0 false
1 8 true

İsimlendirilmiş dönüş değerleri burada belgeleme görevi görür: (lo, hi int, ok bool) imzası hangi değerin ne anlama geldiğini anlatır. ok deseni Go'da yaygındır; map okumasında ve tip iddiasında da aynı biçimi görürsün.

Alıştırma·Fonksiyon zinciri
Orta

Bir []int alıp yeni bir []int döndüren fonksiyonları birleştiren bir chain fonksiyonu yaz. chain(f, g, h) çağrısı, girdiye önce f, sonra g, sonra h uygulayan tek bir fonksiyon döndürsün. Bunu ikiyle çarpma, negatifleri ayıklama ve sıralama adımlarıyla test et.

İpucu

chain bir func([]int) []int döndürmeli. Dönen anonim fonksiyon, yakaladığı fns dilimini gezerek sırayla uygular.

Çözümü göster
main.go
package main

import (
	"fmt"
	"slices"
)

type IntsFunc func([]int) []int

func chain(fns ...IntsFunc) IntsFunc {
	return func(in []int) []int {
		for _, fn := range fns {
			in = fn(in)
		}
		return in
	}
}

func double(in []int) []int {
	out := make([]int, len(in))
	for i, v := range in {
		out[i] = v * 2
	}
	return out
}

func dropNegatives(in []int) []int {
	out := make([]int, 0, len(in))
	for _, v := range in {
		if v >= 0 {
			out = append(out, v)
		}
	}
	return out
}

func sorted(in []int) []int {
	out := slices.Clone(in)
	slices.Sort(out)
	return out
}

func main() {
	pipeline := chain(double, dropNegatives, sorted)

	data := []int{3, -1, 8, -4, 0}
	fmt.Println("girdi: ", data)
	fmt.Println("çıktı: ", pipeline(data))
	fmt.Println("girdi değişmedi:", data)
}
Çıktı
girdi:  [3 -1 8 -4 0]
çıktı:  [0 6 16]
girdi değişmedi: [3 -1 8 -4 0]

Her adım yeni bir dilim üretip girdiyi değiştirmediği için işlem hattı güvenlidir: data en sonda hâlâ ilk hâlindedir. Fonksiyon tipine isim vermek (IntsFunc) imzaları okunur kılar; aksi hâlde func(...func([]int) []int) func([]int) []int gibi bir imza yazman gerekirdi.

Alıştırma·Hız sınırlayıcı closure
Zor

Bir closure kullanarak "her N çağrıdan yalnızca birine izin veren" bir fonksiyon üret: every(3) çağrısı, kendisine yapılan 1., 4., 7. çağrılarda true, diğerlerinde false döndüren bir fonksiyon versin. Ayrıca toplam çağrı sayısını raporlayan ikinci bir fonksiyon da döndür.

İpucu

Bir fonksiyon birden çok fonksiyon döndürebilir ve ikisi de aynı yerel değişkeni yakalayabilir; böylece ortak durumu paylaşırlar.

Çözümü göster
main.go
package main

import "fmt"

// every, N çağrıda bir true döndüren bir fonksiyon ve
// toplam çağrı sayısını veren bir fonksiyon üretir.
func every(n int) (allow func() bool, calls func() int) {
	count := 0
	allow = func() bool {
		count++
		return (count-1)%n == 0
	}
	calls = func() int { return count }
	return allow, calls
}

func main() {
	allow, calls := every(3)

	for i := 1; i <= 10; i++ {
		if allow() {
			fmt.Printf("%d:izin ", i)
		} else {
			fmt.Printf("%d:atla ", i)
		}
	}
	fmt.Println()
	fmt.Println("toplam çağrı:", calls())

	// Bağımsız ikinci sayaç: durumunu paylaşmaz
	allow2, calls2 := every(5)
	allow2()
	allow2()
	fmt.Println("ikinci sayaç:", calls2(), "ilk sayaç:", calls())
}
Çıktı
1:izin 2:atla 3:atla 4:izin 5:atla 6:atla 7:izin 8:atla 9:atla 10:izin
toplam çağrı: 10
ikinci sayaç: 2 ilk sayaç: 10

allow ve calls, every fonksiyonunun yerel değişkeni olan count'u birlikte yakalar. every döndükten sonra bile count yaşamaya devam eder — closure'ların gücü tam olarak budur. Her every çağrısı yeni bir count yarattığı için sayaçlar birbirinden bağımsızdır.

Not: Bu sayaç eşzamanlı çağrılar için güvenli değildir; birden çok goroutine'den çağrılacaksa sync.Mutex gerekir. Bunu Senkronizasyon dersinde göreceksin.

Kısa sınav

Kısa sınav

defer fmt.Println(x) satırından sonra x değiştirilirse ne yazdırılır?

Aynı fonksiyonda üç defer varsa hangi sırayla çalışırlar?

func sum(nums ...int) int fonksiyonuna bir []int dilimi nasıl geçirilir?

Go'da bir fonksiyona struct geçirildiğinde ne olur?

func counter() func() int içinde tanımlanan yerel değişken, fonksiyon döndükten sonra ne olur?

İsimlendirilmiş dönüş değerleri hangi durumda gerçekten gereklidir?

Özet

  • Fonksiyon imzası bir sözleşmedir: func ad(parametreler) (dönüşler). Aynı tipteki parametreler tek seferde yazılabilir.
  • Go'da fonksiyon aşırı yükleme ve varsayılan parametre yoktur; farklı isimler, seçenek struct'ları veya jenerikler kullanılır.
  • Çoklu dönüş, (sonuç, error) desenini mümkün kılar; ilgilenmediğin değeri _ ile atarsın.
  • İsimlendirilmiş dönüşler imzayı belgeler ve defer içinden değiştirilebilir; çıplak return uzun fonksiyonlarda okunabilirliği düşürür.
  • Variadic parametre fonksiyon içinde bir dilimdir; f(s...) çağrısı aynı diziyi paylaşır.
  • Fonksiyonlar birinci sınıf değerlerdir: saklanır, geçirilir, döndürülür. nil bir fonksiyonu çağırmak panik oluşturur.
  • Closure'lar yakaladıkları değişkenin kendisini kullanır ve fonksiyon döndükten sonra da yaşatır; Go 1.22'den beri döngü değişkeni her yinelemede yenidir.
  • defer LIFO sırayla çalışır, argümanlarını hemen değerlendirir ve panikte de çalışır; döngü içinde kullanmaktan kaçın.
  • Go her şeyi değer ile geçirir; çağıranın verisini değiştirmek için işaretçi gerekir.
Bu dersi bitirdin mi?
İlerlemen bu tarayıcıda saklanır.