--- filename: learngo-hu.go contributors: - ["Sonia Keys", "https://github.com/soniakeys"] translators: - ["Szabó Krisztián", "https://github.com/thenonameguy/"] - ["Árpád Goretity", "https://github.com/H2CO3"] --- A Go programozási nyelv az életszerű feladatok könnyebb elvégzése miatt született. A mai legújabb programozási trendeket elkerülve, praktikus megoldást nyújt a valós, üzleti problémákra. C-szerű szintaktikával és statikus típuskezeléssel rendelkezik. A fordító szempillantás alatt végez és egy gyorsan futó,statikus futtatható állományt hoz létre. A nyelv könnyen érthető, folyamatok közötti csatornákon áthaladó üzenetekkel kommunikáló konkurens programozást tesz lehetővé, így könnyen ki lehet használni a mai számítógépek több magos processzorait, ez nagy rendszerek építéséhez ideális. A Go alap könyvtára mindenre területre kiterjed, ennek köszönhetően a nyelvnek egyre növekvő tábora van. ```go // Egy soros komment /* Több soros komment */ // Minden forrás fájl egy csomag-definícióval kezdődik, ez hasonlít a Python // csomagkezelésére // A main egy különleges csomagnév, ennek a fordítása futtatható állományt hoz // létre egy könyvtár helyett. package main // Az import rész meghatározza melyik csomagokat kívánjuk használni ebben a // forrásfájlban import ( "fmt" // A Go alap könyvtárának része "net/http" // Beépített webszerver! "strconv" // Stringek átalakítására szolgáló csomag ) // Függvénydeklarálás, a main nevű függvény a program kezdőpontja. func main() { // Println kiírja a beadott paramétereket a standard kimenetre. // Ha más csomagot függvényeit akarjuk használni, akkor azt jelezni kell a // csomag nevével fmt.Println("Hello world!") // Meghívunk egy másik függvényt ebből a csomagból beyondHello() } // A függvények paraméterei zárójelek között vannak. // Ha nincsenek paraméterek, akkor is kötelező a zárójel-pár. func beyondHello() { var x int // Változó deklaráció, használat előtt muszáj ezt megtenni. x = 3 // Változó értékadás // "Rövid" deklaráció is létezik, ez az érték alapján deklarálja, // definiálja és értéket is ad a változónak y := 4 sum, prod := learnMultiple(x, y) // a függvényeknek több // visszatérési értéke is lehet fmt.Println("sum:", sum, "prod:", prod) // egyszerű kiíratás learnTypes() } // A funkcióknak elnevezett visszatérési értékük is lehet func learnMultiple(x, y int) (sum, prod int) { return x + y, x * y // visszatérünk két értékkel /* sum = x + y prod = x * y return Ez ugyanezzel az eredménnyel járt volna, mint a fenti sor. Üres return esetén, az elnevezett visszatérési változók aktuális értékeikkel térnek vissza. */ } // Beépített típusok func learnTypes() { // Rövid deklarálás az esetek többségében elég lesz a változókhoz s := "Tanulj Go-t!" // string típus s2 := `A "nyers" stringekben lehetnek újsorok is!` // de ettől még ez is ugyanolyan string mint az s, nincs külön // típusa // nem ASCII karakterek. Minden Go forrás UTF-8 és a stringek is azok. g := 'Σ' // rúna(rune) típus, megegyezik az uint32-vel, egy UTF-8 karaktert // tárol f := 3.14159 // float64, az IEEE-754 szabványnak megfelelő 64-bites // lebegőpontos szám c := 3 + 4i // complex128, belsőleg két float64-gyel tárolva // Var szintaxis változótípus-definiálással var u uint = 7 // unsigned, az implementáció dönti el mekkora, akárcsak az // int-nél var pi float32 = 22. / 7 // Rövid deklarásnál átalakítás is lehetséges n := byte('\n') // byte típus, ami megegyezik az uint8-al // A tömböknek fordítás-időben fixált méretük van var a4 [4]int // egy tömb 4 int-tel, mind 0-ra inicializálva a3 := [...]int{3, 1, 5} // egy tömb 3 int-tel, láthatóan inicalizálva egyedi // értékekre // A "szeleteknek" (slices) dinamikus a méretük. A szeleteknek és a tömböknek is // megvannak az előnyeik de a szeleteket sokkal gyakrabban használjuk. s3 := []int{4, 5, 9} // vesd össze a3-mal, nincsenek pontok. s4 := make([]int, 4) // allokál 4 int-et, mind 0-ra inicializálva var d2 [][]float64 // ez csak deklaráció, semmi sincs még allokálva bs := []byte("a slice") // típus konverzió szintaxisa p, q := learnMemory() // deklarál két mutatót (p,q), két int-re fmt.Println(*p, *q) // * követi a mutatót. Ez a sor kiírja a két int értékét. // A map a dinamikusan növelhető asszociatív tömb része a nyelvnek, hasonlít // a hash és dictionary típusokra más nyelvekben. m := map[string]int{"three": 3, "four": 4} m["one"] = 1 // A felhasználatlan változók fordítás-idejű hibát okoznak a Go-ban. // Az aláhúzással "használod" a változókat, de eldobod az értéküket. _, _, _, _, _, _, _, _, _ = s2, g, f, u, pi, n, a3, s4, bs // Kiíratás is természetesen használatnak minősül fmt.Println(s, c, a4, s3, d2, m) learnFlowControl() } // A Go nyelvben szemétgyűjtés (garbage collection) működik. Megtalálhatók benne // mutatók, de nincs pointeraritmetika. Ez azt jelenti, hogy üres (null) mutatóval még // mindig hibázhatsz, de hozzáadni/műveleteket végezni már nem lehet. func learnMemory() (p, q *int) { // Elnevezett visszatérési változóknak int-re mutató a típusa p = new(int) // a beépített "new" funkció, egy típusnak elegendő memóriát // allokál, és visszaad rá egy mutatót. // Az allokált int nullázva van, p többé nem üres mutató. s := make([]int, 20) // allokáljunk 20 int változót egy memóriaterületen. s[3] = 7 // adjunk értéket az egyiknek r := -2 // hozzánk létre egy lokális változót return &s[3], &r // A & megadja a memóriacímét a változónak } func expensiveComputation() int { return 1e6 } func learnFlowControl() { // Az elágazásoknak kötelező a kapcsos zárójel, a zárójel nem szükséges. if true { fmt.Println("megmondtam") } // A kód formátumát a nyelvvel járó "go" parancssori program "go fmt" // parancsa szabványosítja if false { // így lehet } else { // if/else-t csinálni } // Használjunk switchet a hosszabb elágazások alkalmazása helyett. x := 1 switch x { case 0: case 1: // Az "esetek" nem "esnek át", tehát case 2: // ez nem fog lefutni, nincs szükség break-ekre. } // A for ciklus sem használ zárójeleket for x := 0; x < 3; x++ { fmt.Println("iteráció", x) } // itt az x == 1. // A for az egyetlen ciklus fajta a Go-ban, de több formája van. for { // végtelen ciklus break // csak vicceltem continue // soha nem fut le } //Akárcsak a for-nál, az if-nél is lehet rövid deklarálással egy lokális változót létrehozni, //ami a blokk összes if/else szerkezetén keresztül érvényes marad. if y := expensiveComputation(); y > x { x = y } // Függvényeket használhatjuk closure-ként is. xBig := func() bool { return x > 100 // a switch felett deklarált x-et használjuk itt } fmt.Println("xBig:", xBig()) // igaz (utoljára 1e6 lett az értéke az x-nek) x /= 1e5 // így most már x == 10 fmt.Println("xBig:", xBig()) // 10 pedig kisebb mint 100, tehát hamis // Ha nagyon-nagyon szükséges, akkor használhatjuk a jó öreg goto-t. goto love love: learnInterfaces() // Itt kezdődnek az érdekes dolgok! } // Definiáljuk a Stringert egy olyan interfésznek, amelynek egy metódusa van, a // String, ami visszatér egy stringgel. type Stringer interface { String() string } // Definiáljuk a pair-t egy olyan struktúrának amelynek két int változója van, // x és y. type pair struct { x, y int } // Definiáljunk egy metódust a pair struktúrának, ezzel teljesítve a Stringer interfészt. func (p pair) String() string { // p lesz a "fogadó" (receiver) // Sprintf az fmt csomag egy publikus függvénye, műkődése megegyezik a C-s // megfelelőjével. A pontokkal érjük el a mindenkori p struktúra elemeit return fmt.Sprintf("(%d, %d)", p.x, p.y) } func learnInterfaces() { // A kapcsos zárójellel jelezzük, hogy egyből inicializálni // szeretnénk a struktúra változóit a sorrendnek megfelelően. p := pair{3, 4} fmt.Println(p.String()) // meghívjuk a p String metódusát. var i Stringer // deklaráljuk i-t Stringer típusú interfésznek i = p // lehetséges, mert a pair struktúra eleget tesz a // Stringer interfésznek // Meghívjuk i String metódusát, az eredmény ugyanaz, mint az előbb. fmt.Println(i.String()) // Az fmt csomag függvényei automatikusan meghívják a String függvényt // hogy megtudják egy objektum szöveges reprezentációját. fmt.Println(p) // ugyan az az eredmény mint az előbb, a Println meghívja // a String metódust. fmt.Println(i) // dettó learnErrorHandling() } func learnErrorHandling() { // ", ok" szokásos megoldás arra, hogy jól működött-e a függvény. m := map[int]string{3: "three", 4: "four"} if x, ok := m[1]; !ok { // ok hamis lesz, mert az 1 nincs benne a map-ban. fmt.Println("nincs meg") } else { fmt.Print(x) // x lenne az érték, ha benne lenne a map-ban. } // A hiba érték többet is elmond a függvény kimeneteléről, mint hogy minden // "ok" volt-e if _, err := strconv.Atoi("non-int"); err != nil { // _ eldobja az értéket, // úgy se lesz jó jelen // esetben // kiírja, hogy "strconv.ParseInt: parsing "non-int": invalid syntax" fmt.Println(err) } // Az interfészekre még visszatérünk, addig is jöjjön a konkurens programozás! learnConcurrency() } // c egy csatorna, egy konkurens-biztos kommunikációs objektum. func inc(i int, c chan int) { c <- i + 1 // <- a "küldés" operátor, ha a bal oldalán csatorna van, így // i+1-et küld be a csatornába } // Az inc-et fogjuk arra használni, hogy konkurensen megnöveljünk számokat func learnConcurrency() { // Ugyanaz a make függvény, amivel korábban szeleteket hoztunk létre. // A make allokál map-eket, szeleteket és csatornákat. c := make(chan int) // Indítsunk három konkurens goroutine-t. A számok konkurensen lesznek // megnövelve, ha a számítógép képes rá és jól be van állítva, akkor pedig // paralellizálva/egymás mellett. Mind a 3 ugyanabba a csatornába küldi az // eredményeket. go inc(0, c) // A go utasítás indít el goroutine-okat. go inc(10, c) go inc(-805, c) // Beolvassuk 3x a csatornából az eredményeket és kiírjuk őket a kimenetre. // Nem lehet tudni milyen sorrendben fognak érkezni az eredmények! fmt.Println(<-c, <-c, <-c) // hogyha a jobb oldalon csatorna van, akkor a // "<-" a beolvasó/kapó operátor cs := make(chan string) // még egy csatorna, ez stringekkel kommunikál cc := make(chan chan string) // egy csatorna csatornával go func() { c <- 84 }() // indítsunk egy új goroutine-t, csak azért // hogy küldjünk egy számot go func() { cs <- "wordy" }() // ugyanez, csak a cs csatornába stringet // küldünk // A select olyan mint a switch, csak feltételek helyett csatorna műveletek // vannak. Véletlenszerűen kiválasztja az első olyan esetet, ahol létrejöhet // kommunikáció. select { case i := <-c: // a megkapott értéket el lehet tárolni egy változóban fmt.Println("ez egy", i) case <-cs: // vagy el lehet dobni az értékét fmt.Println("ez egy string volt") case <-cc: // üres csatorna, soha nem fog rajta semmi se érkezni fmt.Println("sose futok le :'( ") } // Ezen a ponton vagy c vagy a cs goroutine-ja lefutott. // Amelyik hamarabb végzett, annak a megfelelő case-e lefutott, a másik // blokkolva vár. learnWebProgramming() // a Go képes rá. Te is képes akarsz rá lenni. } // Egy függvény a http csomagból elindít egy webszervert. func learnWebProgramming() { // A ListenAndServe első paramétre egy TCP port, amin kiszolgálunk majd. // Második paramétere egy interfész, pontosabban a http.Handler interfész. err := http.ListenAndServe(":8080", pair{}) fmt.Println(err) // nem felejtjük el kiírni az esetleges hibákat! } // Csináljunk a pair-ból egy http.Handler-t úgy, hogy implementáljuk az // egyetlen metódusát, a ServeHTTP-t. func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Minden kapcsolatra elküldjük ezt a http.ResponseWriter-rel w.Write([]byte("Megtanultad a Go-t Y perc alatt!")) } ``` ## További olvasmányok Minden Go-val kapcsolatos megtaláható a [hivatalos Go weboldalon](https://go.dev/). Ott követhetsz egy tutorialt, játszhatsz a nyelvvel az interneten, és sok érdekességet olvashatsz. A nyelv specifikációját kifejezetten érdemes olvasni, viszonylag rövid és sokat tanul belőle az ember. Ha pedig jobban bele akarod vetni magad a Go-ba, akkor a legjobb praktikákat kilesheted a standard könyvtárból. TIPP: a dokumentációban kattints egy függvény nevére és rögtön megmutatja a hozzá tartozó kódot! Ha pedig a nyelvnek egy bizonyos részéről szeretnél hasonló leírást találni, akkor a [gobyexample.com](https://gobyexample.com/)-on megtalálod, amit keresel.