├── README.md └── effective.md /README.md: -------------------------------------------------------------------------------- 1 | ## Effective Go Cheatsheet 2 | -------------------------------------------------------------------------------- /effective.md: -------------------------------------------------------------------------------- 1 | [Effective-go]("https://golang.org/doc/effective_go.html") sayfasının Türkçe özetidir. 2 | 3 | ## Formatlama 4 | 5 | - Kod stili en çok tartışılan ama çok kritik olmayan sorunlardan biridir 6 | - Go formatlama işini `gofmt` programıyla kendisi hallediyor 7 | 8 | type T struct { 9 | name string // name of the object 10 | value int // its value 11 | } 12 | 13 | - `Gofmt`çalıştırıldıktan sonra: 14 | 15 | type T struct { 16 | name string // name of the object 17 | value int // its value 18 | } 19 | 20 | - Bütün go standart kütüphaneleri `gofmt` ile formatlanmıştır 21 | - Go'da girintileme için tab kullanılıyor 22 | - Herhangi bir satır uzunluğu limiti bulunmuyor 23 | - C ve Java'ya göre söz diziminde daha az parantez kullanıyor 24 | 25 | 26 | ## Yorum 27 | 28 | - C stili blok(/\*\*/) ve satır(//) şeklinde açıklama yazılabiliyor 29 | - Blok açıklama satırları `paket` tanımlamalarında kullanılıyor 30 | - `paket` içeriği ile ilgili dokümantasyon oluşturulurken blok açıklamalarından yararlanıyor 31 | 32 | 33 | ## İsimlendirme 34 | 35 | - Değişken isimleri konvansiyon olarak `mixedCaps` ya da `MixedCaps` şeklinde olmalı 36 | - Go'da metodların private ya da public olmasını isimleri belirliyor 37 | - Büyük harfle başlayan metodlar public, küçük harfle başlayanlar ise private 38 | 39 | Topla(a int, b int) // public 40 | topla(a int, b int) // private 41 | ### Paket isimleri 42 | 43 | ### Getter/Setter 44 | 45 | - Go'da getter ve setter yapısı yok 46 | - `owner` adında bir değişken için `getOwner` değil `Owner` adında bir metod yazmak yeterli 47 | 48 | ### Interface isimleri 49 | 50 | - Tek metodluk interface'ler metod_ismi + `er` şeklinde isimlendirilebilir (Reader, Writer) 51 | 52 | ## Noktalı virgül 53 | 54 | - C'deki gibi Go'da da satırlar noktalı virgül(;) ile sonlanıyor 55 | - Tek fark Go'da bu işlem derleme öncesinde kaynak kod taranırken otomatik yapılıyor 56 | - Bu özelliğin yan etkisi olarak kıvrık parantezler 1TBS olmak zorunda 57 | - 1 True Brace Style: 58 | 59 | if i < f() { 60 | g() 61 | } 62 | ## Kontrol yapıları 63 | 64 | - `if` basit hali: 65 | 66 | if x > 0 { 67 | return y 68 | } 69 | 70 | - `if` içinde değişken ilklenebilir: 71 | 72 | if err := file.Chmod(0664); err != nil { 73 | log.Print(err) 74 | return err 75 | } 76 | 77 | - Koşul ifadelerindeki gereksiz `else` ifadeleri kullanmak zorunda değilsiniz 78 | 79 | f, err := os.Open(name) 80 | if err != nil { 81 | return err 82 | } 83 | codeUsing(f) 84 | 85 | - Aşağıdaki örnek kod akışında koşullar sağlanmadığı durumda `return`'lar devreye girip akışı bitirecek. 86 | 87 | f, err := os.Open(name) 88 | if err != nil { 89 | return err 90 | } 91 | d, err := f.Stat() 92 | if err != nil { 93 | f.Close() 94 | return err 95 | } 96 | codeUsing(f, d) 97 | 98 | 99 | ## Değişken tanımlama ve Atama 100 | 101 | 102 | ## For 103 | 104 | - Go'daki `for` C'deki `for`ve `while`'ı kapsıyor 105 | - Go'da `do-while` yok 106 | - `for`'un üç tipi var 107 | 108 | // C'deki for tarzı 109 | for init; condition; post { } 110 | 111 | // C'deki while 112 | for condition { } 113 | 114 | // C'deki for(;;) 115 | for { } 116 | 117 | - Kısa atama özelliği index değişkenini ilklendirme işini kolaylaştırıyor 118 | 119 | sum := 0 120 | for i := 0; i < 10; i++ { 121 | sum += i 122 | } 123 | 124 | - Array, map, slice, string üzerinde iterasyon yapmanız gerekiyorsa `range` fonksiyonu oldukça kullanışlı 125 | 126 | for key, value := range oldMap { 127 | newMap[key] = value 128 | } 129 | 130 | - Sadece indeks ya da anahtar bilgisi gerekiyorsa 131 | 132 | for key := range m { 133 | if key.expired() { 134 | delete(m, key) 135 | } 136 | } 137 | 138 | - Sadece mapteki değerler lazımsa `_` kullanarak istenmeyen değeri boşa çıkarabilirsiniz 139 | 140 | sum := 0 141 | for _, value := range array { 142 | sum += value 143 | } 144 | 145 | - `range` string'lerin içinde de dolaşmaya olanak sağlıyor 146 | 147 | for pos, char := range "日本\x80語" { // \x80 is an illegal UTF-8 encoding 148 | fmt.Printf("character %#U starts at byte position %d\n", char, pos) 149 | } 150 | 151 | çıktısı 152 | 153 | character U+65E5 '日' starts at byte position 0 154 | character U+672C '本' starts at byte position 3 155 | character U+FFFD '�' starts at byte position 6 156 | character U+8A9E '語' starts at byte position 7 157 | 158 | - Go'da `++` ve `--` operatörleri yok 159 | - Paralel atamaya olanak sağlıyor 160 | 161 | // 'ters' --> 'srst' 162 | for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 { 163 | a[i], a[j] = a[j], a[i] 164 | } 165 | 166 | ## Switch 167 | 168 | func unhex(c byte) byte { 169 | switch { 170 | case '0' <= c && c <= '9': 171 | return c - '0' 172 | case 'a' <= c && c <= 'f': 173 | return c - 'a' + 10 174 | case 'A' <= c && c <= 'F': 175 | return c - 'A' + 10 176 | } 177 | return 0 178 | } 179 | 180 | - Koşul durumları liste olarak verilebilir 181 | 182 | func shouldEscape(c byte) bool { 183 | switch c { 184 | case ' ', '?', '&', '=', '#', '+', '%': 185 | return true 186 | } 187 | return false 188 | } 189 | 190 | - Go'da `break`, `switch`'i sonlardırmak için kullanılabilir 191 | - Döngü ya da `switch`'in hangisinin sonlanacağına etiket kullanılarak karar verilebilir 192 | 193 | Loop: 194 | for n := 0; n < len(src); n += size { 195 | switch { 196 | case src[n] < sizeOne: 197 | if validateOnly { 198 | break 199 | } 200 | size = 1 201 | update(src[n]) 202 | 203 | case src[n] < sizeTwo: 204 | if n+1 >= len(src) { 205 | err = errShortInput 206 | break Loop 207 | } 208 | if validateOnly { 209 | break 210 | } 211 | size = 2 212 | update(src[n] + src[n+1]< b 222 | func Compare(a, b []byte) int { 223 | for i := 0; i < len(a) && i < len(b); i++ { 224 | switch { 225 | case a[i] > b[i]: 226 | return 1 227 | case a[i] < b[i]: 228 | return -1 229 | } 230 | } 231 | switch { 232 | case len(a) > len(b): 233 | return 1 234 | case len(a) < len(b): 235 | return -1 236 | } 237 | return 0 238 | } 239 | 240 | ## Tip switch'i 241 | 242 | - `switch` kullanarak bir değişkenin tipini öğrenebilirsiniz 243 | 244 | var t interface{} 245 | t = functionOfSomeType() 246 | switch t := t.(type) { 247 | default: 248 | fmt.Printf("unexpected type %T", t) // %T prints whatever type t has 249 | case bool: 250 | fmt.Printf("boolean %t\n", t) // t has type bool 251 | case int: 252 | fmt.Printf("integer %d\n", t) // t has type int 253 | case *bool: 254 | fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool 255 | case *int: 256 | fmt.Printf("pointer to integer %d\n", *t) // t has type *int 257 | } 258 | 259 | ## Fonksiyonlar 260 | 261 | - Fonksiyonlar birden fazla değer dönebilir 262 | - Örneğin, `Write` metodu hem satır sayısı hemde hata mesajı dönebilir 263 | 264 | func (file *File) Write(b []byte) (n int, err error) 265 | 266 | - Dönüş değerleride aynı fonksiyon paremetleri gibi isimlendirilebilir 267 | - Tanımlanan değişkenler tiplerine göre sıfırla ilklendirilir 268 | 269 | func ReadFull(r Reader, buf []byte) (n int, err error) { 270 | for len(buf) > 0 && err == nil { 271 | var nr int 272 | nr, err = r.Read(buf) 273 | n += nr 274 | buf = buf[nr:] 275 | } 276 | return 277 | } 278 | 279 | ## Defer 280 | 281 | - `defer` verilen fonksiyonun, `defer`'i çağıran fonksiyondan `return` edilince çağrılmasını sağlar 282 | 283 | // Dosya içeriğini string olarak döner. 284 | func Contents(filename string) (string, error) { 285 | f, err := os.Open(filename) 286 | if err != nil { 287 | return "", err 288 | } 289 | defer f.Close() // işimiz biter bitmez f.Close çalışacak. 290 | var result []byte 291 | buf := make([]byte, 100) 292 | for { 293 | n, err := f.Read(buf[0:]) 294 | result = append(result, buf[0:n]...) // append daha sonra inelecek. 295 | if err != nil { 296 | if err == io.EOF { 297 | break 298 | } 299 | return "", err // burdan çıkarsa f kapatılacak. 300 | } 301 | } 302 | return string(result), nil // burdan çıkarsada f kapatılacak. 303 | } 304 | 305 | - Yukarıdaki örnekte `defer`kullanmanın iki avantajı var: 306 | - dosyayı kapatmayı unutma gibi bir durum kalmıyor 307 | - açma ve kapama işlemleri birbirine yakın olduğundan okunması rahat hale geliyor 308 | 309 | - `defer` çağrıları LIFO şeklinde çalışır 310 | 311 | for i := 0; i < 5; i++ { 312 | defer fmt.Printf("%d ", i) // 4 3 2 1 0 sırasıyla yazacak 313 | } 314 | 315 | 316 | 317 | ## `new` ile bellek ayırma 318 | 319 | - Bellek ayırma iki fonksiyon yardımı ile yapılabilir. `new` ve `make` 320 | - `new` ile ayırılan alan verilen tipin sıfırı ile ilklendirilir 321 | - `new` ayrılan alanın adresini döner 322 | 323 | p := new(SyncedBuffer) // *SyncedBuffer 324 | var v SyncedBuffer // SyncedBuffer 325 | 326 | - Ayrılan alanın ilklendirilmesi gereken durumlarda olabilir 327 | 328 | func NewFile(fd int, name string) *File { 329 | if fd < 0 { 330 | return nil 331 | } 332 | f := new(File) 333 | f.fd = fd 334 | f.name = name 335 | f.dirinfo = nil 336 | f.nepipe = 0 337 | return f 338 | } 339 | 340 | - En basit şekilde(`composite literal`) kullanarakta aynı işlemi gerçekleştirebiliriz 341 | 342 | func NewFile(fd int, name string) *File { 343 | if fd < 0 { 344 | return nil 345 | } 346 | f := File{fd, name, nil, 0} 347 | return &f 348 | } 349 | 350 | - C'nin tersine Go'da yerel değişkenin adresini dönebilirsiniz 351 | - `composite literal` her seferinde bellekten yeni bir alan ayırır. 352 | - Değişken fonksiyondan çıkıldıktan sonrada hafızada kalacaktır 353 | 354 | return &File{fd, name, nil, 0} 355 | 356 | - Yukarıdaki şekilde fonksiyonu daha da sade bir hale getirebiliriz 357 | - Paremeteler verilirken sıralı şekilde verilmelidir 358 | - Ya da `değişken_ismi: değer` şeklinde istediğiniz sırada verebilirsiniz 359 | 360 | return &File{fd: fd, name: name} 361 | 362 | - Aynı şekilde array, slice, map gibi veri türlerini de oluşturabilirsiniz 363 | 364 | a := [...]string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} 365 | s := []string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"} 366 | m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"} 367 | 368 | ## `make` ile bellek ayırma 369 | 370 | - `make(T, args)`, `new(T)`'den farklı olarak ayırdığı alanı ilkler ve T tipinde bir değer döner(\*T değil) 371 | - Örneğin: 372 | 373 | make([]int, 10, 100) 374 | 375 | - Bellekte 100 int'lik bir alan ayırır, sonra 100 intlik dizinin ilk 10 elemanını işaret eden, boyutu 10 olan ve 100 kapasiteli bir `slice` oluşturur 376 | - Aşağıdaki örnekte `new` ile `make`'in arasındaki farkı daha iyi anlayabiliriz: 377 | 378 | var p *[]int = new([]int) // slice veriyapısı oluşturuyor; *p == nil; nadiren kullanışlı 379 | var v []int = make([]int, 100) // 100 karakterlik bir int dizisine işaret eden bir v slice'ı oluşturur 380 | 381 | // Gereksiz karmaşık bellek ayırma: 382 | var p *[]int = new([]int) 383 | *p = make([]int, 100, 100) 384 | 385 | // Idiomatic: 386 | v := make([]int, 100) 387 | 388 | - `make` sadece map, slice, channel için kullanılabilir. Ve pointer dönmez. 389 | 390 | ## Array 391 | 392 | - Array'ler ne kadar hafızaya ihtiyaç duyulduğu bilinen durumlarda kullanışlı olabilirler 393 | - Genelde bir sonraki bölümde anlatılacak slice veri yapısının alt yapısını oluştururlar 394 | - C'deki array'lerden farklıdırlar 395 | * Array'ler birer değer olarak tutulur. Bir array'i diğerine atamak, array'in kopyalanmasına neden olur 396 | * Aynı şekilde bir fonksiyona array verirseniz, fonksiyon array'in bir kopyasını alacaktır(pointer değil) 397 | * Array'lerin boyutu tiplerinin bir parçasıdır. Yani [10]int ile [20]int farklı türlerdir 398 | 399 | - C'deki gibi kullanmak isterseniz 400 | 401 | func Sum(a *[3]float64) (sum float64) { 402 | for _, v := range *a { 403 | sum += v 404 | } 405 | return 406 | } 407 | 408 | array := [...]float64{7.0, 8.5, 9.1} 409 | x := Sum(&array) // adres öperatörüne dikkat edin 410 | 411 | - Yukarıdaki kullanımda idiomatic bir yaklaşım değil. Go'da yaygın olarak slice'lar kullanılır 412 | 413 | ## Slice 414 | 415 | - Slice'lar dinamik array'ler gibi düşünülebilir 416 | - Her slice arka tarafta bir array'e işaret eder 417 | - Bir slice'ı diğer bir slice'a atarsanız ikiside aynı array'a işaret edeceğinden kopyalanma olmaz 418 | - Aynı şekilde fonksiyona parametre olarak verilidiğinde adres olarak geçer 419 | - Arkadaki array'in kapasitesi kadar dinamik olarak boyutları ihtiyaca göre artabilir 420 | - Aşağıdaki kod parçasında `append` fonksiyonun nasıl çalıştığını görebilirsiniz 421 | 422 | func Append(slice, data[]byte) []byte { 423 | l := len(slice) 424 | if l + len(data) > cap(slice) { // veri kapasiteden daha büyükse belleği arttır 425 | // Gerekenin iki katı kadar alan ayır, gelcekte oluşabilecek ihtiyaçlar için 426 | newSlice := make([]byte, (l+len(data))*2) 427 | // copy, built-in bir fonskiyondur ve her slice türü için kullanılabilir. 428 | copy(newSlice, slice) 429 | slice = newSlice 430 | } 431 | slice = slice[0:l+len(data)] 432 | for i, c := range data { 433 | slice[l+i] = c 434 | } 435 | return slice 436 | } 437 | 438 | - Slice'ler adress bazlı çalıştıkları için yeni oluşan slice'in fonksiyondan dönülmesi gerekiyor 439 | 440 | ## İki boyutlu slice'lar 441 | 442 | - Go'daki array ve slice veri türleri tek boyutludur 443 | - Çoklu boyutta slice veya array tanımlamak için: 444 | 445 | type Transform [3][3]float64 // 3x3 array. 446 | type LinesOfText [][]byte // slice 447 | 448 | - Slice'ların boyutları tuttukları verilere göre değiştiği için iç içe olan slice'ların boyutları farklı olabilir 449 | 450 | text := LinesOfText{ 451 | []byte("Now is the time"), 452 | []byte("for all good gophers"), 453 | []byte("to bring some fun to the party."), 454 | } 455 | 456 | - İki boyutlu slice'lar için bellek ayırma işlemini iki şekilde yapabilirsiniz 457 | - İlk olarak en dıştaki slice için ayırıp, sonrasında her satır için tek tek ayırırsınız 458 | 459 | // En dıştaki slice için bellek ayır. 460 | picture := make([][]uint8, YSize) // her y birim için bir satır. 461 | // Her satır için dolaş ve ayırma işlemini yap. 462 | for i := range picture { 463 | picture[i] = make([]uint8, XSize) 464 | } 465 | 466 | - İkinci yol 467 | 468 | // En dıştaki slice için bellek ayır. 469 | picture := make([][]uint8, YSize) // her y birim için bir satır. 470 | // Bütün pixelleri tutması için büyük bir slice oluştur. 471 | pixels := make([]uint8, XSize*YSize) // []uint8 tipinde resim [][]uint8 tipinde olduğu halde. 472 | // Döngü ile dolaşıp pixels slice içindeki her satır için ayırma işlemini yap. 473 | for i := range picture { 474 | picture[i], pixels = pixels[:XSize], pixels[XSize:] 475 | } 476 | 477 | ## Maps 478 | 479 | - Anahtar, değer ikilisini tutan veri yapısıdır 480 | - Slice hariç eşitlik özelliğini destekleyen bütün veri tipleri anahtar olarak kullanılabilir 481 | - Map'lerde Slice'lar gibi arka planda bir veri yapısını gösterirler 482 | - Bundan dolayı bir fonksiyona map geçirilirse yapılan değişiklik kalıcı olur 483 | - Tanımlama: 484 | 485 | var timeZone = map[string]int{ 486 | "UTC": 0*60*60, 487 | "EST": -5*60*60, 488 | "CST": -6*60*60, 489 | "MST": -7*60*60, 490 | "PST": -8*60*60, 491 | } 492 | şeklinde yapılabilir 493 | - Değer atasması yapma ya da anahtara gelen değeri çekme işkemi slice ve array'lerde olduğu gibi yapılabilir 494 | 495 | offset := timeZone["EST"] 496 | 497 | - Eğer map'te olmayan bir anahtar için değer çekilmeye çalışırsanız, veri türüne göre sıfır tipi dönecektir(Integer ise 0, string ise "" gibi) 498 | - Set veri türü değer kümesi `bool` tipli olan bir map ile gerçeklenebilir 499 | 500 | 501 | attended := map[string]bool{ 502 | "Ann": true, 503 | "Joe": true, 504 | ... 505 | } 506 | 507 | if attended[person] { // eğer map'te person diye biri yoksa false dönecektir 508 | fmt.Println(person, "toplantıdaydı") 509 | } 510 | 511 | - Bazen anahtara karşılık gelen değer boş string("") ya da sıfır olabilir 512 | - Bu durumda anahtar olmadığı için bir sıfır tipi dönüyor yoksa gerçekten tutulan değer mi sıfır diye bir ayrım yapmanız gerekcektir 513 | 514 | var seconds int 515 | var ok bool 516 | seconds, ok = timeZone[tz] 517 | 518 | - Bu yapıya "virgül, ok" deniyor 519 | - Eğer `tz` diye bir anahtar var ise ok true olacaktır, aksi takdirde false 520 | - Aşağıdaki örnekte fonksiyon içinde nasıl kullanıldığını görebilirsiniz 521 | 522 | func offset(tz string) int { 523 | if seconds, ok := timeZone[tz]; ok { 524 | return seconds 525 | } 526 | log.Println("Bilinmeyen zaman dilimi:", tz) 527 | return 0 528 | } 529 | - Değeri önemsemeksizin test amaçlı sorgulamak için `_` öperatörünü kullanabilirsiniz 530 | 531 | _, present := timeZone[tz] 532 | 533 | - Map'ten bir değer silmek içinde yerel `delete` fonksiyonunu kullanabilirsiniz 534 | 535 | delete(timeZone, "PDT") 536 | 537 | 538 | 539 | --------------------------------------------------------------------------------