Megy

Az oldal jelenlegi verzióját még nem ellenőrizték tapasztalt közreműködők, és jelentősen eltérhet a 2022. szeptember 4-én felülvizsgált verziótól ; az ellenőrzések 4 szerkesztést igényelnek .
Megy
Nyelvóra többszálú , imperatív , strukturált , objektumorientált [1] [2]
A végrehajtás típusa összeállított
Megjelent 2009. november 10
Szerző Robert Grismer , Rob Pike és Ken Thompson
Fejlesztő Google , Rob Pike , Ken Thompson , The Go Authors [d] és Robert Grismer [d]
Fájlkiterjesztés _ .go
Kiadás
Típusrendszer szigorú , statikus , típuskövetkeztetéssel
Befolyásolva C [4] , Oberon-2 , Limbo , Active Oberon , Sequential Process Interaction Theory , Pascal [4] , Oberon [4] , Smalltalk [5] , Newsqueak [d] [6] , Modula-2 [6] , Alef [d] , APL [7] , BCPL , Modula és Occam
Engedély BSD
Weboldal go.dev
OS DragonFly BSD , FreeBSD , Linux , macOS , NetBSD , OpenBSD , Plan 9 , Solaris , Microsoft Windows , iOS , Android , AIX és Illumos
 Médiafájlok a Wikimedia Commons oldalon

A Go (gyakran a golang is ) a Google által belsőleg kifejlesztett többszálú , lefordított programozási nyelv [8] . A Go fejlesztése 2007 szeptemberében kezdődött, és Robert Grismer , Rob Pike és Ken Thompson [9] , aki korábban az Inferno operációs rendszer fejlesztési projektjén dolgozott, közvetlenül részt vett annak tervezésében . A nyelvet hivatalosan 2009 novemberében vezették be . Jelenleg a nyelv készítői által kifejlesztett hivatalos fordítóprogram támogatása a FreeBSD , OpenBSD , Linux , macOS , Windows , DragonFly BSD , Plan 9 , Solaris , Android , AIX operációs rendszerekhez biztosított . [10] . A Go-t a gcc fordítókészlet is támogatja , és számos független megvalósítás létezik. A nyelv második változata fejlesztés alatt áll.

Cím

A Google által választott nyelv neve majdnem megegyezik a Go! programozási nyelv nevével! , készítette F. Gee. McCabe és C. L. Clark 2003-ban [11] . A nevet a Go oldalon tárgyaljuk [11] .

A nyelv honlapján és általában az internetes kiadványokban gyakran használják a "golang" alternatív nevet.

Cél, ideológia

A Go nyelvet programozási nyelvként fejlesztették ki rendkívül hatékony programok létrehozására, amelyek modern elosztott rendszereken és többmagos processzorokon futnak. Kísérletként fogható fel a C és C++ nyelvek helyettesítésének megteremtésére , figyelembe véve a megváltozott számítástechnikai technológiákat és a nagy rendszerek fejlesztésében felhalmozott tapasztalatokat [12] . Rob Pike [12] szavaival élve: "A Go-t arra tervezték, hogy megoldja a valós szoftverfejlesztési problémákat a Google-nál." A fő problémákként a következőket sorolja fel:

A nyelvvel szemben támasztott fő követelmények a következők voltak [13] :

A Go-t azzal az elvárással hozták létre, hogy a rajta lévő programokat objektumkódba fordítsák le, és közvetlenül, virtuális gép nélkül hajtsák végre , így az architekturális megoldások kiválasztásának egyik kritériuma a hatékony objektumkódba való gyors fordítás és a túlzott mennyiség hiánya volt. dinamikus támogatás követelményei.

Az eredmény egy nyelv, "ami nem volt áttörés, de ennek ellenére kiváló eszköz volt nagy szoftverprojektek fejlesztéséhez" [12] .

A Go-hoz ugyan elérhető interpreter , de gyakorlatilag nincs rá nagy szükség, hiszen a fordítási sebesség elég gyors ahhoz, hogy interaktív fejlesztést tegyen lehetővé.

A nyelv főbb jellemzői

A Go nyelv főbb jellemzői [9] :

A Go nem tartalmazza a többi modern alkalmazásprogramozási nyelvben elérhető népszerű szintaktikai funkciókat. Ezt sok esetben a fejlesztők tudatos döntése okozza. A választott tervezési döntések rövid indoklása a nyelvről szóló "Gyakran Ismételt Kérdésekben" [9] található, részletesebben - a nyelv honlapján megjelent cikkekben és vitákban, különféle tervezési lehetőségeket mérlegelve. Különösen:

Szintaxis

A Go nyelv szintaxisa hasonló a C nyelvéhez, az Oberontól és a szkriptnyelvektől kölcsönzött elemekkel .

Ábécé

A Go egy kis- és nagybetűket megkülönböztető nyelv, amely teljes Unicode-támogatást nyújt a karakterláncokhoz és azonosítókhoz.

Az azonosító hagyományosan bármilyen nem üres betűből, számból és aláhúzásból álló sorozat lehet, amely betűvel kezdődik, és nem egyezik a Go kulcsszavak egyikével sem. A "betűk" az összes Unicode karakterre utalnak, amelyek a "Lu" (nagybetűk), "Ll" (kisbetűk), "Lt" (nagybetűk), "Lm" (módosító betűk) vagy "Lo" ( egyéb betűk), a "számok" alatt - az "Nd" kategória összes karaktere (számok, tizedesjegyek). Így semmi sem akadályozza meg például a cirill betű használatát az azonosítókban.

Azok az azonosítók, amelyek csak kis- és nagybetűben különböznek egymástól, különböznek egymástól. A nyelvnek számos konvenciója van a kis- és nagybetűk használatára vonatkozóan. A csomagnevekben különösen csak kisbetűket használnak. Minden Go kulcsszó kisbetűvel van írva. A nagybetűvel kezdődő változók exportálhatók (nyilvános), a kisbetűkkel kezdődő változók pedig nem exportálhatók (privát).

A string literálok korlátozás nélkül használhatják az összes Unicode karaktert . A karakterláncok UTF-8 karaktersorozatokként vannak ábrázolva .

Csomagok

Bármely Go program egy vagy több csomagot tartalmaz. A csomagot, amelyhez a forráskód fájl tartozik, a fájl elején található csomagleírás adja meg. A csomagnevekre ugyanazok a korlátozások vonatkoznak, mint az azonosítókra, de csak kisbetűket tartalmazhatnak. A gorutin csomagrendszernek a könyvtárfához hasonló fastruktúrája van. Bármely globális objektum (változók, típusok, interfészek, függvények, metódusok, struktúrák és interfészek elemei) korlátozás nélkül elérhető abban a csomagban, amelyben deklarálták. A nagybetűvel kezdődő globális objektumok exportálhatók.

Egy másik csomag által exportált objektumok Go kódfájlban való használatához a csomagot a következővel kell importálni import.

csomag /* Import */ import ( "fmt" // Szabványos csomag a formázott kimenethez "database/sql" // Beágyazott csomag importálása w "os" // Importálás aliassal . "math" // Importálás minősítés nélkül _ használatakor "gopkg.in/goracle.v2" // A csomag kódjában nincs kifejezett hivatkozás ) func main () { for _ , arg := range w . Args { // Az "os" csomagban deklarált Args tömb elérése az fmt álnéven keresztül . Println ( arg ) // Az "fmt" csomagban deklarált Println() függvény meghívása } var db * sql csomagnévvel . db = sql . Megnyitás ( driver , dataSource ) // A beágyazott csomagból származó nevek minősítése // csak a csomag neve (sql) x := Sin ( 1.0 ) // a math.Sin() hívása - minősítés a csomagnév alapján math nem szükséges // mert név nélkül importált // A kódban nincs utalás a "goracle.v2" csomagra, de be lesz importálva. }

Felsorolja a forrásfa src könyvtárából importált csomagok elérési útját, amelyek pozícióját a környezeti változó adja meg GOPATH, míg a szabványos csomagok esetében csak a nevet adja meg. A csomagot azonosító karakterláncot egy álnév előzheti meg, ebben az esetben a csomag neve helyett kódban kerül felhasználásra. Az importált objektumok teljes minősítéssel (például " ") állnak rendelkezésre az őket importáló fájlban пакет.Объект. Ha egy csomagot álnév helyett ponttal importálnak, akkor az általa exportált összes név minősítés nélkül elérhető lesz. Ezt a funkciót egyes rendszersegédprogramok használják, de a programozó általi használata nem javasolt, mivel az explicit minősítés védelmet nyújt a névütközések és a kód viselkedésének "észrevehetetlen" változásai ellen. Nem lehet minősítés nélkül importálni két azonos nevű csomagot.

A csomagok importálása a Go-ban szigorúan ellenőrzött: ha egy csomagot egy modul importál, akkor legalább egy, a csomag által exportált nevet fel kell használni a modul kódjában. A Go fordító egy nem használt csomag importálását hibaként kezeli; egy ilyen megoldás arra kényszeríti a fejlesztőt, hogy folyamatosan frissítse az import listákat. Ez nem okoz nehézséget, mivel a Go programozást támogató eszközök (szerkesztők, IDE-k) általában az importlisták automatikus ellenőrzését és frissítését biztosítják.

Ha egy csomag olyan kódot tartalmaz, amelyet csak introspekcióval használunk , akkor egy probléma adódik: egy ilyen csomag importálása szükséges ahhoz, hogy szerepeljen a programban, de a fordító nem engedélyezi, mivel közvetlenül nem érhető el. _Az ilyen esetekben a névtelen importálás biztosított: „ ” (egy aláhúzás) álnévként van megadva ; egy ilyen módon importált csomag akkor kerül lefordításra és bekerül a programba, ha a kódban kifejezetten nem hivatkozik rá. Egy ilyen csomag azonban nem használható kifejezetten; ez megakadályozza az importvezérlés megkerülését az összes csomag névtelen importálásával.

A futtatható Go programnak tartalmaznia kell egy main nevű csomagot, aminek tartalmaznia kell egy main()paraméter nélküli függvényt és egy visszatérési értéket. A függvény main.main()a "program törzse" - kódja a program indulásakor fut le. Bármely csomag tartalmazhat függvényt init() – lefut a program betöltésekor, a végrehajtás megkezdése előtt, mielőtt bármelyik függvényt meghívnák ebben a csomagban és minden olyan csomagban, amely ezt importálja. A fő csomag mindig utoljára inicializálódik, és minden inicializálás megtörténik a függvény végrehajtása előtt main.main().

Modulok

A Go csomagolási rendszert azzal a feltételezéssel tervezték, hogy a teljes fejlesztési ökoszisztéma egyetlen fájlfaként létezik, amely tartalmazza az összes csomag legfrissebb verzióit, és amikor új verziók jelennek meg, teljesen újrafordítják. A harmadik féltől származó könyvtárakat használó alkalmazásprogramozásnál ez meglehetősen erős korlátozás. A valóságban gyakran vannak korlátozások az egyik vagy másik kód által használt csomagok verzióira, valamint olyan helyzetekre, amikor egy projekt különböző verziói (ágai) a könyvtári csomagok különböző verzióit használják.

Az 1.11-es verzió óta a Go támogatja az úgynevezett modulokat . A modul egy speciálisan leírt csomag, amely információkat tartalmaz a verziójáról. A modul importálásakor a használt verzió rögzítve van. Ez lehetővé teszi a build rendszer számára, hogy szabályozza, hogy minden függőség teljesül-e, automatikusan frissítse az importált modulokat, amikor a szerző kompatibilis módosításokat hajt végre rajtuk, és blokkolja a nem visszafelé kompatibilis verziók frissítését. A modulok állítólag megoldást (vagy sokkal könnyebb megoldást) jelentenek a függőségkezelés problémájára.

Megjegyzések és pontosvesszők

A Go mindkét típusú C-stílusú megjegyzést használja: a soron belüli megjegyzéseket (a // ... karakterrel kezdődően) és a blokkolt megjegyzéseket (/* ... */). A soros megjegyzést a fordító újsorként kezeli. Blokk, amely egy sorban található - szóközként, több sorban - újsorként.

A pontosvessző a Go-ban kötelező elválasztóként használatos bizonyos műveleteknél (ha, for, switch). Formálisan is be kell fejeznie az egyes parancsokat, de a gyakorlatban nem szükséges ilyen pontosvesszőt a sor végére tenni, mivel a fordító maga ad pontosvesszőt minden sor végére, az üres karakterek kivételével a sor végére. azonosító, szám, karakter literál, karakterlánc, törés, folytatás, átfutás, visszatérési kulcsszavak, növelő vagy csökkentő parancs (++ vagy --), vagy záró zárójel, négyzet vagy kapcsos zárójel (fontos kivétel, hogy a vessző nem szerepel a fenti listán). Ebből két dolog következik:

  • A gyakorlatban a pontosvesszőre csak bizonyos if, for switch utasítások és az ugyanazon sorban található parancsok elválasztására van szükség. Ezért a Go kódban nagyon kevés pontosvessző található.
  • A fordító általi automatikus pontosvesszővé tétel mellékhatása, hogy a programban sehol, ahol szóköz megengedett, nem lehet sortörést használni. Különösen a leírásokban, inicializálási parancsokban és ha kapcsolószerkezeteknél nem helyezheti át a nyitó kapcsos zárójelet a következő sorba:
func g () // ! { // ROSSZ } ha x { } // ! más { // ROSSZ } func g (){ // JOBBRA } ha x { } else { // IGAZ } Itt az első két esetben pontosvesszőt szúr be a fordító a felkiáltójeles megjegyzéssel jelölt sorba, mivel a sor (szóközök és megjegyzések figyelmen kívül hagyásával) kerek, illetve göndör zárójelekkel végződik. Ennek eredményeként az első esetben a függvénydeklaráció szintaxisa, a második esetben a feltételes operátor szintaxisa megszakad. Hasonlóképpen, a vesszővel elválasztott elemek listájában nem helyezheti át a vesszőt a következő sorba: func f ( i // ! , k int // ! , s // ! , t string ) string { // ROBBAN } func f ( i , k int , s , t string ) string { // TRUE } Ha vesszőt viszünk át a következő sorba, az aktuális sor egy azonosítóval végződik, és a végére automatikusan pontosvessző kerül, ami sérti a lista szintaxisát (a vessző, mint fentebb említettük, kivétel a szabály alól; a fordító nem tesz utána pontosvesszőt). Így a nyelv egy bizonyos kódírási stílust diktál. A Go fordító a gofmt segédprogrammal érkezik, amely biztosítja a forrásszövegek helyes és egységes formázását. A Go szabványkönyvtárban található összes szöveget ez a segédprogram formázza.

Beépített adattípusok

A nyelv egyszerű beépített adattípusok meglehetősen szabványos halmazát tartalmazza: egész számokat, lebegőpontos számokat, karaktereket, karakterláncokat, logikai értékeket és néhány speciális típust.

Egész számok

11 egész számtípus létezik:

  • Fix méretű előjeles egész számok - int8, int16, int32, int64. Ezek kettős komplemensben ábrázolt előjeles egészek , az ilyen típusú értékek mérete rendre 8, 16, 32, 64 bit. Az értéktartomány -2 n-1 és 2 n-1 -1 között van, ahol n a típus mérete.
  • Fix méretű előjel nélküli egész számok - uint8, uint16, uint32, uint64. A típusnévben szereplő szám az előző esethez hasonlóan a méretet adja meg, de az értéktartomány 0 és 2 n −1 között van.
  • intés uint előjeles és előjel nélküli egész számok. Ezeknek a típusoknak a mérete megegyezik, és lehet 32 ​​vagy 64 bites, de a nyelvi specifikáció nem rögzíti, és a megvalósítás választhatja. Feltételezhető, hogy a célplatformon a leghatékonyabb méretet választják ki számukra.
  • byte - szinonimája uint8. Rendszerint formázatlan bináris adatokkal való munkavégzésre szolgál.
  • rune a szinonimája uint32, egy Unicode karaktert jelent.
  • uintptr egy előjel nélküli egész érték, amelynek mérete a megvalósítás által meghatározott, de elég nagynak kell lennie ahhoz, hogy egy ilyen típusú változóban tárolja a célplatform teljes mutatóértékét.

A nyelv készítői a programon belüli számokkal való munkavégzéshez csak a szabványos típus használatát javasolják int. A rögzített méretű típusokat úgy tervezték, hogy a külső forrásokból származó vagy átadott adatokkal dolgozzanak, amikor a kód helyessége érdekében fontos a típus egy adott méretének megadása. A típusok szinonimák byte, és runebináris adatokkal, illetve szimbólumokkal való együttműködésre készültek. A típus uintptrcsak külső kóddal való interakcióhoz szükséges, például C-ben.

Lebegőpontos számok

A lebegőpontos számokat kétféle, float32és float64. Méretük 32, illetve 64 bites, a megvalósítás megfelel az IEEE 754 szabványnak . Az értéktartomány a standard csomagból szerezhető be math.

Numerikus típusok korlátlan pontossággal

A Go standard könyvtára tartalmazza a csomagot is big, amely három típust biztosít korlátlan pontossággal: big.Int, big.Ratés big.Float, amelyek rendre egész számokat, racionális számokat és lebegőpontos számokat jelentenek; ezeknek a számoknak a mérete bármi lehet, és csak a rendelkezésre álló memória mennyisége korlátozza. Mivel a Go operátorai nincsenek túlterhelve, a számokkal végzett korlátlan pontosságú számítási műveletek szokásos módszerekként valósulnak meg. A nagy számokkal végzett számítások teljesítménye természetesen lényegesen gyengébb a beépített numerikus típusokéhoz képest, de bizonyos típusú számítási feladatok megoldásánál a csomag bighasználata előnyösebb lehet, mint egy matematikai algoritmus kézi optimalizálása.

Komplex számok

A nyelv két beépített típust is biztosít a komplex számokhoz, complex64és complex128. Ezen típusok mindegyik értéke egy valós és képzeletbeli részpárt tartalmaz, amelyek típusai, float32illetve float64. Kétféleképpen hozhat létre összetett típusú értéket a kódban: vagy egy beépített függvény complex()használatával, vagy egy képzeletbeli literál használatával egy kifejezésben. Egy komplex szám valós és imaginárius részeit a real()és függvényekkel kaphatja meg imag().

var x complex128 = complex ( 1 , 2 ) // 1 + 2i y := 3 + 4i // 3 + 4i , ahol a 4 egy szám , amelyet egy i utótag követ // egy képzeletbeli fmt literál . Println ( x * y ) // kiírja a "(-5+10i)" fmt-t . Println ( valós ( x * y )) // kiírja a "-5" fmt . Println ( imag ( x * y )) // "10"-et nyomtat Logikai értékek

A logikai típus boolmeglehetősen gyakori - magában foglalja az előre meghatározott értékeket , valamint az igaz trueés falsehamis jelölést. A C-vel ellentétben a Go logikai értékei nem numerikusak, és nem konvertálhatók közvetlenül számokká.

Strings

A karakterlánc típusú értékek stringmegváltoztathatatlan bájttömbök, amelyek UTF-8. Ez a karakterláncok számos sajátos jellemzőjét okozza (például általános esetben egy karakterlánc hossza nem egyenlő az őt reprezentáló tömb hosszával, azaz a benne lévő karakterek száma nem egyenlő a számmal bájtok száma a megfelelő tömbben). A legtöbb teljes karakterláncot feldolgozó alkalmazásnál ez a specifikum nem fontos, de olyan esetekben, amikor a programnak közvetlenül meghatározott rúnákat (Unicode karaktereket) kell feldolgoznia, szükség van egy csomagra unicode/utf8, amely a Unicode karakterláncokkal való munkavégzéshez szükséges segédeszközöket tartalmazza.

Típusdeklarációk

Bármilyen adattípushoz, beleértve a beépítetteket is, új analóg típusok deklarálhatók, amelyek megismétlik az eredetiek összes tulajdonságát, de nem kompatibilisek velük. Ezek az új típusok opcionálisan deklarálhatnak metódusokat is. A felhasználó által definiált adattípusok a Go-ban: mutatók (szimbólummal deklarálva *), tömbök (szögletes zárójelben), struktúrák ( struct), függvények ( func), interfészek ( interface), leképezések ( map) és csatornák ( chan). E típusok deklarációi megadják elemeik típusát, esetleg azonosítóit. Új típusok deklarálása a következő kulcsszóval történik type:

type PostString string // Írja be a "string"-et, hasonlóan a beépítetthez type StringArray [] string // Tömbtípus string típusú elemekkel type Person struct { // Struktúratípus neve string // a szabványos karakterlánc típus mezője post PostString // a korábban deklarált egyéni karakterlánc típus mezője bdate time . Time // Time típusú mező, a csomagból importálva time edate time . Időfőnök * Személy // mutatómező következtetés [ ]( * Személy ) // tömb mező } típus InOutString chan string // csatorna típusa karakterláncok átadásához típus CompareFunc func ( a , b interfész {}) int // függvénytípus.

A Go 1.9-es verziója óta a típusálnevek (aliasok) deklarálása is elérhető:

type TitleString = string // A "TitleString" a beépített karakterlánc típusú álneve Integer = int64 // Az "Integer" a beépített 64 bites egész típus álneve

Az álnév deklarálható egy rendszertípushoz vagy bármely felhasználó által meghatározott típushoz. Az alapvető különbség az álnevek és a közönséges típusdeklarációk között, hogy a deklaráció egy új típust hoz létre, amely nem kompatibilis az eredetivel, még akkor is, ha a deklarációban nincs módosítás az eredeti típushoz. Az álnév ugyanannak a típusnak csak egy másik neve, ami azt jelenti, hogy az álnév és az eredeti típus teljesen felcserélhető.

A szerkezeti mezők leírásában lehetnek címkék – tetszőleges karaktersorozatok hátsó idézőjelbe zárva:

// Struktúra mezőcímkékkel típusú XMLInvoices struct { XMLName xml . Név `xml:"INVOICES"` Verzió int `xml:"version,attr"` Számla [] * XMLInvoice `xml:"SZÁMLA"` }

reflectA címkéket a fordító figyelmen kívül hagyja, de a róluk szóló információkat elhelyezi a kódban, és a szabványos könyvtárban található csomag funkcióival kiolvasható . A címkéket jellemzően típusfelosztás biztosítására használják adatok külső adathordozón való tárolására és visszaállítására, vagy olyan külső rendszerekkel való interakcióra, amelyek saját formátumukban fogadják vagy továbbítják az adatokat. A fenti példa a szabványos könyvtár által feldolgozott címkéket használ az adatok XML formátumban történő olvasására és írására.

Változódeklarációk

A változók deklarálásának szintaxisát elsősorban a Pascal szellemében oldják meg: a deklaráció a var kulcsszóval kezdődik, ezt követi az elválasztón keresztül a változó neve, majd az elválasztón keresztül a típusa.

Megy C++
var v1 int const v2 string var v3 [ 10 ] int var v4 [ ] int var v5 struct { f int } var v6 * int /* mutató aritmetika nem támogatott */ var v7 map [ string ] int var v8 func ( a int ) int int v1 ; const std :: stringv2 ; _ /* ról ről */ intv3 [ 10 ] ; int * v4 ; /* ról ről */ struct { int f ; } v5 ; int * v6 ; std :: rendezetlen_térkép v7 ; /* ról ről */ int ( * v8 )( int a );

A változó deklaráció kombinálható inicializálással:

var v1 int = 100 var v2 string = "Szia!" var v3 [ 10 ] int = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } var v4 [ ] int = { 1000 , 2000 , 12334 } var v5 struct { f int } = 50 } var v6 * int = & v1 var v7 map [ string ] int = { "egy" : 1 , "kettő" : 2 , "három" : 3 } var v8 func ( a int ) int = func ( a int ) int { return a + 1 }

Ha egy változót nem inicializálunk kifejezetten a deklaráláskor , akkor az automatikusan "null értékre" inicializálódik az adott típushoz. A null értéke minden számtípusnál 0, típusnál ez string az üres karakterlánc, mutatóknál  pedig nil. A struktúrák alapértelmezés szerint nulla értékkészlettel vannak inicializálva az egyes mezőkhöz, a tömbelemek pedig a tömbdefinícióban megadott típusú nulla értékekkel.

A hirdetések csoportosíthatók:

var ( i int m float )

Automatikus típuskövetkeztetés

A Go nyelv az automatikus típuskövetkeztetést is támogatja . Ha egy változót a deklaráláskor inicializálunk, akkor a típusa elhagyható - a hozzárendelt kifejezés típusa a változó típusává válik. A literálok (számok, karakterek, karakterláncok) esetében a nyelvi szabvány meghatározott beépített típusokat határoz meg, amelyekhez minden ilyen érték tartozik. Más típusú változó inicializálásához explicit típuskonverziót kell alkalmazni a literálra.

var p1 = 20 // p1 int - a 20 egész literál int típusú. var p2 = uint ( 20 ) // p2 uint - explicit módon uintra öntött érték. var v1 = & p1 // A v1 *int a p1-re mutató mutató, amelyhez az int típust vezetjük le. var v2 = & p2 // v2 *uint egy mutató a p2-re, amely kifejezetten előjel nélküli egész számként van inicializálva.

A helyi változók esetében létezik egy rövidített deklarációs forma, amelyet típuskövetkeztetést használó inicializálással kombinálnak:

v1 := 100 v2 := "Szia!" v3 := [ 10 ] int { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 } v4 := [] int { 1000 , 2000 , 12334 } v5 := }{ struct { f int 50 } v6 := és v1

Feladatok

A Go a szimbólumot használja hozzárendelési operátorként =:

a = b // Állítsa be az a változót b-re

Mint fentebb említettük, létezik egy változó definiálása automatikus típuskövetkeztetéssel, inicializálással kombinálva, amely külsőleg a Pascal -beli hozzárendeléshez hasonlít :

v1 := v2 // hasonló a var v1 = v2-hez

A Go fordító szigorúan nyomon követi a definíciókat és hozzárendeléseket, és megkülönbözteti őket a másiktól. Mivel az azonos nevű változó újradefiniálása egy hatókörben, egy kódblokkon belül tilos, egy változó :=csak egyszer jelenhet meg a jeltől balra:

a := 10 // Egész változó deklarálása és inicializálása a. b := 20 // Egész változó deklarálása és inicializálása b. ... a := b // HIBA! Próbálja meg újradefiniálni a.

A Go több hozzárendelés párhuzamos végrehajtását teszi lehetővé:

i , j = j , i // i és j értékek felcserélése.

Ebben az esetben a hozzárendelési jeltől balra lévő változók számának pontosan meg kell egyeznie a hozzárendelési jeltől jobbra lévő kifejezések számával.

A párhuzamos hozzárendelés is lehetséges a :=. Különlegessége, hogy a jeltől balra felsorolt ​​változók :=között előfordulhatnak már létező változók. Ebben az esetben új változók jönnek létre, a meglévők újra felhasználásra kerülnek. Ezt a szintaxist gyakran használják hibakezelésre:

x , err := SomeFunction () // A függvény két értéket ad vissza (lásd lent), // két változót deklarál és inicializál. if ( err != nil ) { return nil } y , err := SomeOtherFunction () // Itt csak y van deklarálva, az err egyszerűen hozzárendel egy értéket.

A példa utolsó sorában a függvény által visszaadott első érték az új y változóhoz, a második a már meglévő változóhoz van hozzárendelve err, amely a kódban a meghívott függvények által visszaadott utolsó hiba elhelyezésére szolgál. Ha nem ez a tulajdonsága az operátornak :=, akkor a második esetben egy új változót (például err2) kell deklarálni, vagy külön deklarálni y, majd a szokásos párhuzamos hozzárendelést kell használni.

A Go "hozzárendelésre másolás" szemantikát valósít meg, ami azt jelenti, hogy a hozzárendelés az eredeti változó értékének másolatát, majd a másolatot egy másik változóba helyezi, ami után a változók értékei eltérnek, és megváltoztatják az egyik változót. nem változtatják meg a másikat. Ez azonban csak az adott hosszúságú beépített skaláris típusokra, struktúrákra és tömbökre igaz (vagyis azokra a típusokra, amelyek értékei a veremben vannak lefoglalva). A halomban határozatlan hosszúságú tömbök és leképezések vannak lefoglalva, az ilyen típusú változók tulajdonképpen objektumokra való hivatkozásokat tartalmaznak, hozzárendelésükkor csak a hivatkozást másolja át, magát az objektumot nem. Néha ez nem várt hatásokhoz vezethet. Tekintsünk két szinte egyforma példát:

type vector [ 2 ] float64 // A tömb hossza kifejezetten be van állítva v1 := vector { 10 , 15.5 } // Inicializálás - a v1 magát a tömböt tartalmazza v2 := v1 // A v1 tömb a v2 v2 tömbbe van másolva [ 0 ] = 25.3 // Csak a v2 fmt tömb változott . Println ( v1 ) // "[10 15.5]" kinyomtatása – az eredeti tömb nem változott. fmt . Println ( v2 ) // "[25.3 15.5]" nyomtatása

Itt a típust vectorkét számból álló tömbként határozzuk meg. Az ilyen tömbök hozzárendelése ugyanúgy működik, mint a számok és struktúrák hozzárendelése.

A következő példában pedig a kód pontosan egy karakterrel különbözik: a típus vectoregy határozatlan méretű tömbként van definiálva. De ez a kód teljesen másként viselkedik:

type vector [] float64 // Meghatározatlan hosszúságú tömb v1 := vector { 10 , 15.5 } // Inicializálás - v1 tömbhivatkozást tartalmaz v2 := v1 // A tömbhivatkozás a v1-ből a v2-be másolódik v2 [ 0 ] = 25.3 / / Úgy képzelhető el, hogy csak a v2 fmt tömb változott . Println ( v1 ) // "[25.3 15.5]" kinyomtatása - az eredeti tömb VÁLTOZOTT! fmt . Println ( v2 ) // "[25.3 15.5]" nyomtatása

A leképezések és interfészek ugyanúgy viselkednek, mint a második példában. Sőt, ha a struktúrának van referencia vagy interfész típusú mezője, vagy egy mező dimenzió nélküli tömb vagy leképezés, akkor egy ilyen struktúra hozzárendelésekor csak a hivatkozás kerül másolásra, vagyis a különböző struktúrák mezői kezdődnek. hogy ugyanazokra a tárgyakra mutasson a memóriában.

E hatás elkerülése érdekében kifejezetten olyan rendszerfüggvényt kell használnia, copy()amely garantálja az objektum második példányának létrehozását.

A függvények és módszerek argumentumai

így deklarálják:

func f ( i , j , k int , s , t string ) string { }

A függvények több értéket is visszaadhatnak

Az ilyen értékek típusai zárójelben vannak:

func f ( a , b int ) ( int , string ) { return a + b , "kiegészítés" }

A függvény eredményeit el is nevezhetjük:

func incTwo ( a , b int ) ( c , d int ) { c = a + 1 d = b + 1 return }

A megnevezett eredményeket közvetlenül a függvényfejléc után deklaráltnak tekintjük nulla kezdeti értékkel. Az ilyen függvényben lévő return utasítás paraméterek nélkül használható, ebben az esetben a függvényből való visszatérés után az eredmények a végrehajtás során hozzárendelt értékekkel rendelkeznek. Tehát a fenti példában a függvény a paramétereinél eggyel nagyobb egész értékpárt ad vissza.

A függvények által visszaadott több értéket vesszővel elválasztva kell a változókhoz hozzárendelni, míg a függvényhívás eredményeként hozzárendelt változók számának pontosan meg kell egyeznie a függvény által visszaadott értékek számával:

first , second := incTwo ( 1 , 2 ) // first = 2, second = 3 first := incTwo ( 1 , 2 ) // ROSSZ - nincs változó hozzárendelve a második eredményhez

"_" pszeudováltozó

Ellentétben a Pascal-lal és a C-vel, ahol a helyi változó későbbi felhasználása nélküli deklarálása, vagy a helyi változó értékének elvesztése (amikor a változóhoz rendelt érték nem olvasható sehol) csak fordítói figyelmeztetést okozhat, Go-ban ez a helyzet figyelembe vehető. nyelvi hiba, és a program fordításának ellehetetlenüléséhez vezet. Ez különösen azt jelenti, hogy a programozó nem hagyhatja figyelmen kívül a függvény által visszaadott értéket (vagy valamelyik értéket), egyszerűen csak hozzárendeli azt valamilyen változóhoz, és megtagadja a további használatát. Ha szükségessé válik a függvényhívás által visszaadott értékek egyikének figyelmen kívül hagyása, egy előre definiált "_" nevű pszeudováltozó (egy aláhúzás) kerül felhasználásra. Bárhol megadható, ahol legyen olyan változó, amely értéket vesz fel. A megfelelő érték nem lesz hozzárendelve egyetlen változóhoz sem, és egyszerűen elvész. Egy ilyen építészeti döntésnek az a célja, hogy a fordítási szakaszban azonosítsa a számítási eredmények esetleges elvesztését: az értékfeldolgozás véletlen kihagyását észleli a fordító, és a „_” pszeudováltozó használata azt jelzi, hogy a programozó szándékosan figyelmen kívül hagyta az eredményeket. A következő példában, ha az incTwo függvény által visszaadott két érték közül csak az egyikre van szükség, a második változó helyett "_"-t kell megadni:

first := incTwo ( 1 , 2 ) // INVALID first , _ := incTwo ( 1 , 2 ) // TRUE, a második eredmény nincs felhasználva

A "_" változó tetszőleges számú alkalommal megadható a hozzárendelési listában. A „_”-nak megfelelő függvényeredmények figyelmen kívül maradnak.

Elhalasztott hívási mechanizmus elhalasztása

A halasztott hívás egyszerre több szintaktikai jellemzőt helyettesít, különösen a kivételkezelőket és a garantált befejezési blokkokat. A defer kulcsszó előtti függvényhívás paraméterezése a program azon a pontján történik, ahová elhelyezték, és közvetlenül azelőtt fut le, hogy a program kilép a hatókörből, ahol deklarálták, függetlenül attól, hogy ez a kilépés hogyan és milyen okból történik. Ha egy függvény több halasztási deklarációt tartalmaz, a megfelelő hívások egymás után, a függvény lejárta után, fordított sorrendben kerülnek végrehajtásra. Az alábbiakban egy példa látható a halasztás garantált befejezési blokkként való használatára [15] :

// A fájlt másoló függvény func CopyFile ( dstName , srcName string ) ( int64 írás , err hiba ) { src , err : = os . Megnyitás ( srcName ) // Nyissa meg a forrásfájlt, ha err != nil { // Ellenőrizze a return -t // Ha nem sikerül, térjen vissza hibával } // Ha ide jutott, akkor a forrásfájl megnyitása sikeres volt defer src . Close () // Késleltetett hívás: src.A Close() akkor kerül meghívásra, amikor a CopyFile befejeződik dst , err := os . Create ( dstName ) // Célfájl megnyitása , ha err != nil { // Ellenőrzés és visszatérés hiba esetén return } defer dst . Close () // Késleltetett hívás: A dst.Close() meghívásra kerül, amikor a CopyFile befejeződik returnio . _ Másolás ( dst , src ) // Adatok másolása és visszatérés a függvényből // Miután minden művelet meg lesz hívva: először dst.Close(), majd src.Close() }

Hurok és elágazás feltételei

A legtöbb C-szerű szintaxisú nyelvtől eltérően a Go nem rendelkezik zárójelekkel a feltételes konstrukciókhoz for, if, switch:

if i >= 0 && i < len ( arr ) { println ( arr [ i ]) } ... for i := 0 ; i < 10 ; i ++ { } }

Ciklusok

A Go egy hurokkonstrukciót használ mindenféle hurok megszervezéséhez  for.

i < 10 { // hurok esetén előfeltétellel, hasonlóan a C- ben leírtakhoz for i := 0 ; i < 10 ; i ++ { // ciklus számlálóval, hasonló a for-hoz a C-ben } for { // végtelen ciklus // A ciklusból való kilépést manuálisan kell kezelni, // általában return vagy break } for { // ciklus utófeltétellel ... // ciklustörzs if i >= 10 { // kilépési feltétel törése } } for i , v := range arr { // hurok a gyűjteményben (tömb, szelet, megjelenítés) arr // i - az aktuális elem indexe (vagy kulcsa) // v - az aktuális tömbelem értékének másolata } for i := range arr { // hurok a gyűjteményben, csak az index kerül felhasználásra } _ , v := tartomány arr { // cikluson keresztül a gyűjtésen keresztül, csak elemértékeket használjon } for range arr { // Végigpörgetés a gyűjteményben változók nélkül (a gyűjtemény // csak iterációs számlálóként használható). } v := c tartomány esetén { // hurok a csatornán keresztül: // v a c csatorna értékeit olvassa ki, // amíg a csatornát be nem zárja egy párhuzamos // gorutin }

Többválasztós operátor

A feleletválasztós operátor szintaxisának switchszámos jellemzője van. Először is, a C-vel ellentétben nem szükséges az operátor használata break: a kiválasztott ág feldolgozása után az operátor végrehajtása véget ér. Ha éppen ellenkezőleg, azt szeretné, hogy a következő ág a kiválasztott elágazás után folytassa a feldolgozást, akkor a következő operátort kell használnia fallthrough:

kapcsoló értéke { 1. eset : fmt . Println ( "One" ) fallthrough // Ezután a "case 0:" ág kerül végrehajtásra case 0 : fmt . println ( "nulla" ) }

Itt, amikor value==1két sor jelenik meg, az "Egy" és a "Zero".

A switch utasításban szereplő választási kifejezés és ennek megfelelően az alternatívák tetszőleges típusúak lehetnek, egy ágban több opció is felsorolható:

karaktereket váltani [ kód ]. category { case "Lu" , "Ll" , "Lt" , "Lm" , "Lo" : ... case "Nd" : ... default : ... }

Választási kifejezés hiánya megengedett, ebben az esetben logikai feltételeket kell beírni az alternatívákba. Az első ág végrehajtásra kerül, amelynek feltétele igaz:

switch { case '0' <= c && c <= '9' : return c - '0' case 'a' <= c && c <= 'f' : return c - 'a' + 10 case 'A' <= c && c <= 'F' : return c - 'A' + 10 }

Egy fontos részlet: ha az egyik feltételű ág az operátorral végződik fallthrough, akkor ez után az ág után a következő kerül feldolgozásra, függetlenül attól, hogy a feltétele teljesül-e . Ha azt szeretné, hogy a következő ág csak akkor kerüljön feldolgozásra, ha a feltétele teljesül, akkor szekvenciális konstrukciókat kell használnia if.

Építészeti jellemzők

Hibák és kivételek kezelése

A Go nyelv nem támogatja a legtöbb modern nyelvre jellemző strukturált kivételkezelési szintaxist , amely egy speciális paranccsal (általában throwvagy raise) kivételeket dob, és blokkban kezeli őket try-catch. Ehelyett ajánlatos a hibavisszaadást használni a függvény egyik eredményeként (ami praktikus, mivel a Go-ban egy függvény egynél több értéket is visszaadhat):

  • Az utolsó paraméterben a függvény hibaobjektumot vagy nullmutatót ad vissza, nilha a függvényt hiba nélkül hajtották végre. A hiba típusa általában egy könyvtári felület error.
  • A függvény által visszaadott objektumot a rendszer ellenőrzi, és a hibát, ha van, kezeli. Ha egy hiba a hívás helyén nem kezelhető megfelelően, akkor az általában az aktuális függvény eredményeként kerül visszaadásra, vagy ez alapján új hiba keletkezik és visszaadja.
func ReadFile ( srcName string )( result string , err error ) { file , err := os . Nyissa meg ( srcName ) if err != nil { // Új hiba létrehozása minősítő szöveggel return nil , fmt . Errorf ( "fájl %q: %w olvasása" , srcName , err ) } ... // A függvény további végrehajtása, ha nem volt hiba , eredményt ad vissza , nil // Eredmény és üres hiba, ha sikeres }
  • Lehetetlen figyelmen kívül hagyni a függvény által visszaadott hibát (a fenti példában ne ellenőrizze a változó értékét err), mert a változó további használat nélküli inicializálása a Go nyelvben fordítási hibához vezet. Ez a korlátozás megkerülhető az err pszeudováltozó helyettesítésével _, de ez egyértelműen nyilvánvaló, ha a kódot nézzük.

A nyelv számos kritikusa úgy véli, hogy ez az ideológia rosszabb, mint a kivételkezelés, mivel számos ellenőrzés összezavarja a kódot, és nem teszi lehetővé, hogy minden hibakezelés blokkokban összpontosuljon catch. A nyelv alkotói ezt nem tartják komoly problémának. Számos hibakezelési mintát írnak le a Go programban (lásd például Rob Pike cikkét a hivatalos Go blogon , orosz fordítás ), amelyek csökkenthetik a hibakezelési kód mennyiségét.

Amikor végzetes hibák fordulnak elő, amelyek lehetetlenné teszik a további programvégrehajtást (például nullával való osztás vagy tömbhatárok elérése), pánikállapot lép fel , ami alapértelmezés szerint a program összeomlásához vezet egy hibaüzenettel és egy hívási verem nyomkövetésével. deferA pánikokat a fent leírt késleltetett végrehajtási konstrukció segítségével lehet elkapni és kezelni . A -ban megadott függvényhívás deferaz aktuális hatókör elhagyása előtt történik, beleértve a pánikhelyzetet is. A behívott függvényen belül deferhívhatunk egy szabványos függvényt recover() - leállítja a pánik rendszer feldolgozását és visszaadja annak okát erroregy normál hibaként feldolgozható objektum formájában. De a programozó egy korábban elkapott pánikot is folytathat a szabvány hívásával panic(err error).

// A program elvégzi az első paraméterének egész számmal történő osztását // a másodikkal // és kiírja az eredményt. func main () { defer func () { err := recovery () if v , ok := err .( error ); ok { // Az fmt interfész hibájának megfelelő pánik kezelése . Fprintf ( os . Stderr , "Error %v \"%s\"\n" , err , v . Error ()) } else if err != nil { pánik ( err ) // Váratlan hibák kezelése - emelje újra a pánik. } }() a , err := strconv . ParseInt ( os . Args [ 1 ], 10 , 64 ) if err != nil { pánik ( err ) } b , err := strconv . ParseInt ( os . Args [ 2 ], 10 , 64 ) if err != nil { panic ( err ) } fmt . fprintf ( os . Stdout , "%d / %d = %d\n " , a , b , a / b ) }

A fenti példában hibák fordulhatnak elő, amikor a függvény a program argumentumait egész számokká konvertálja strconv.ParseInt(). Pánikba eshet az os.Args tömb elégtelen számú argumentumával történő elérésekor is, vagy nullával való osztásakor, ha a második paraméter nulla. Bármilyen hibahelyzet esetén pánik keletkezik, amelyet a hívás feldolgoz defer:

> oszd el 10 5-tel 10/5 = 2 > oszd el a 10 0-t Hiba runtime.errorString "futásidejű hiba: egész szám osztása nullával" > oszd el 10,5 2-vel Hiba *strconv.NumError "strconv.ParseInt: "10.5" elemzés: érvénytelen szintaxis > oszd el a 10-et Hiba runtime.errorString "futásidejű hiba: az index tartományon kívül van"

A pánik nem váltható ki az egyik párhuzamosan végrehajtott gorutinban (lásd alább), de kezelhető egy másikban. Szintén nem ajánlott a pánikot a csomaghatáron átlépni.

Többszálú

A Go szálfűzési modelljét az Active Oberon nyelvtől örökölték, amely Tony Hoare CSP -jén alapul, az Occam és a Limbo nyelv ötleteit felhasználva [9] , de olyan funkciók is jelen vannak, mint a pi-calculus és a csatornázás.

A Go segítségével új programvégrehajtási szálat hozhat létre a go kulcsszó használatával , amely névtelen vagy elnevezett függvényt futtat egy újonnan létrehozott gorutinban (a Go kifejezés a coroutines -okra ). Ugyanazon a folyamaton belül minden gorutin közös címteret használ, az operációs rendszer szálain fut, de az utóbbihoz való kemény kötődés nélkül, ami lehetővé teszi a futó gorutin számára, hogy blokkolt gorutinnal rendelkező szálat hagyjon (például várjon üzenet küldésére vagy fogadására). csatornából ) és folytassa tovább. A futásidejű könyvtár tartalmaz egy multiplexert, amely megosztja a rendelkezésre álló számú rendszermagot a gorutinok között. Lehetőség van korlátozni azon fizikai processzormagok maximális számát, amelyeken a program végrehajtásra kerül. A gorutinok Go futásidejű könyvtár általi öntámogatása megkönnyíti a hatalmas számú gorutin használatát a programokban, ami messze meghaladja a rendszer által támogatott szálak számának korlátját.

func server ( i int ) { for { print ( i ) time . Sleep ( 10 ) } } go szerver ( 1 ) go szerver ( 2 )

A lezárások használhatók egy go kifejezésben .

var g int go func ( i int ) { s := 0 j := 0 esetén ; j < i ; j ++ { s += j } g = s } ( 1000 )

A gorutinok közötti kommunikációhoz csatornákat használnak (a beépített chan típus ), amelyeken keresztül tetszőleges érték továbbítható. Egy csatornát a beépített függvény hoz létre make(), amely átadja a csatorna típusát és (opcionálisan) hangerejét. Alapértelmezés szerint a csatorna hangereje nulla. Az ilyen csatornák puffermentesek . A csatorna tetszőleges pozitív egész térfogatát beállíthatja, ekkor egy pufferelt csatorna jön létre.

Egy puffereletlen cső szorosan szinkronizálja az olvasó és író szálakat a segítségével. Amikor egy írószál ír valamit egy csőbe, az megáll, és megvárja, amíg az érték beolvasásra kerül. Amikor egy olvasószál megpróbál beolvasni valamit egy olyan csőből, amelyre már írtak, beolvassa az értéket, és mindkét szál folytathatja a végrehajtást. Ha még nincs érték írva a csatornára, az olvasószál szünetel, és várja, hogy valaki írjon a csatornára. Vagyis a nem pufferolt csövek a Go nyelvben ugyanúgy viselkednek, mint az Occam vagy az Ada nyelvben a randevúzási mechanizmus .

A pufferelt csatornának van egy értékpuffere, amelynek mérete megegyezik a csatorna méretével. Amikor egy ilyen pipere ír, az érték a cső pufferébe kerül, és az író szál szünet nélkül folytatódik, kivéve, ha a cső puffere megtelt az írás idején. Ha a puffer megtelt, akkor az író szál felfüggesztésre kerül mindaddig, amíg legalább egy értéket ki nem olvas a csatornából. Az olvasószál egy pufferelt csőből is kiolvas egy értéket szünet nélkül, ha olvasatlan értékek vannak a cső pufferében; Ha a csatornapuffer üres, akkor a szál szünetel, és megvárja, amíg valamelyik másik szál értéket ír neki.

Ha kész, a csatorna bezárható a beépített funkcióval close(). A privát csatornára való írási kísérlet pánikot okoz, a privát csatornáról történő olvasás mindig szünet nélkül történik, és az alapértelmezett értéket olvassa be. Ha a csatorna pufferelt, és a bezáráskor N korábban írt értéket tartalmaz a pufferben, akkor az első N olvasási művelet úgy történik, mintha a csatorna még nyitva lenne, és kiolvassa az értékeket a pufferből, és csak ezután a csatorna kiolvasása adja vissza az alapértelmezett értékeket.

A művelet egy érték átadására szolgál egy csatornának és egy csatornáról <-. Csatornára írásakor bináris operátorként, olvasáskor unáris operátorként használják:

in := make ( chan string , 0 ) // Puffer nélküli csatorna létrehozása in out := make ( chan int , 10 ) // Pufferelt csatorna létrehozása out ... in <- arg // Írjon egy értéket a csatornának ... r1 := <- ki // kiolvasás a csatornából ... r2 , ok := <- ki // olvasás a csatorna zárásának ellenőrzésével ha ok { // ha ok == igaz - a csatorna nyitva van ... } else { // ha a csatorna be van zárva, csinálj valami mást ... }

A csatornáról történő olvasás műveletének két lehetősége van: ellenőrzés nélkül és a csatorna bezárásának ellenőrzésével. Az első opció (a fenti példában r1 olvasása) egyszerűen beolvassa a következő értéket a változóba; ha a csatorna zárva van, akkor az alapértelmezett érték lesz beolvasva az r1-be. A második opció (r2 olvasása) az érték mellett egy logikai értéket is beolvas - az ok csatorna állapotjelző jelzőt, amely akkor lesz igaz, ha a csatornából kiolvasták a stream által odahelyezett adatokat, és hamis, ha a csatorna zárva és a puffere üres. Ezzel a művelettel az olvasószál meg tudja határozni, hogy a bemeneti csatorna mikor van zárva.

A csőből történő olvasás a for-range hurokkonstrukcióval is támogatott:

// A függvény a bemeneti csatornából párhuzamosan egész számokkal kezdi a olvasást és a kimeneti csatornára csak azokat az egészeket // írja, amelyek pozitívak. // Visszaadja a kimeneti csatornát. func pozitívok ( in <- chan int64 ) <- chan int64 { out := make ( chan int64 ) go func ( ) { // A hurok addig folytatódik , amíg be nem záródik a következő := range in { if next > 0 { ki <- következő } } bezár ( ki ) }( ) visszatér }

A CSP mellett vagy a csatornázási mechanizmussal együtt a Go azt is lehetővé teszi, hogy a szálak szinkronizált interakciójának szokásos modelljét használja a megosztott memórián keresztül, tipikus hozzáférési szinkronizálási eszközökkel, például mutexekkel . Ugyanakkor a nyelvi specifikáció óva int a párhuzamos szálak osztott memórián keresztüli szinkronizálatlan interakciójának minden kísérletétől, mivel kifejezett szinkronizálás hiányában a fordító optimalizálja az adathozzáférési kódot anélkül, hogy figyelembe venné az egyidejű hozzáférés lehetőségét a különböző forrásokból. szálakat, ami váratlan hibákhoz vezethet. Például előfordulhat, hogy egy szálban a globális változók értékeinek írása nem vagy rossz sorrendben látható egy párhuzamos szálból.

Vegyük például az alábbi programot. A függvény kódja main()abból a feltételezésből indul ki, hogy a gorutinban elindított függvény setup()létrehoz egy típusú struktúrát T, inicializálja a "hello, world" karakterlánccal, majd hivatkozást rendel az inicializált szerkezetre a globális változóhoz g. B main()elindít egy üres ciklust, és vár egy gnem nulla érték megjelenésére. Amint megjelenik, main()kiír egy karakterláncot a mutatott struktúrából g, feltételezve, hogy a szerkezetet már inicializálták.

írja be a T struct { msg string } varg * T _ func setup () { t : = new ( T ) t . msg = "helló, világ" g = t } func main () { go setup () for g == nil { // NEM MŰKÖDIK!!! } nyomtatás ( g . üzenet ) }

A valóságban két hiba egyike lehetséges.

  • Előfordulhat, hogy a főszál egyszerűen nem látja a változást a változóban g, és a program egy végtelen ciklusban lefagy. Ez akkor fordulhat elő, ha egy agresszív optimalizálásra beállított fordító megállapítja, hogy a létrehozott setup()értéket nem adják át sehova, és egyszerűen eltávolítja a függvény teljes kódját, mint jelentéktelent.
  • A főszál látni fogja, hogy az érték gmár nem nulla, de az érték nem inicializálódik g.msga függvény végrehajtása során; print()ebben az esetben a program egy üres karakterláncot ad ki. Ez akkor fordulhat elő, ha a fordító optimalizálás céljából eltávolítja a helyi változót t, és közvetlenül a létrehozott objektumra ír hivatkozást g.

Az osztott memórián keresztüli adatátvitel megszervezésének egyetlen helyes módja a könyvtári szinkronizálási eszközök használata, amelyek garantálják, hogy az egyik szinkronizált adatfolyam által a szinkronizálási pont előtt írt összes adat garantáltan elérhető lesz egy másik szinkronizált adatfolyamban a szinkronizálási pont után.

A Go-ban a többszálú kezelés egyik jellemzője, hogy a gorutin semmilyen módon nem azonosítható, és nem olyan nyelvi objektum, amelyre függvények hívásakor hivatkozni lehet, vagy amely konténerbe helyezhető. Ennek megfelelően nincsenek olyan eszközök, amelyek lehetővé tennék a korutin végrehajtásának azon kívülről történő közvetlen befolyásolását, mint például a felfüggesztés, majd az indítás, a prioritás megváltoztatása, az egyik korutin befejezésének megvárása a másikban, illetve a végrehajtás erőszakos megszakítása. Bármilyen művelet a gorutinon (a főprogram leállításán kívül, amely automatikusan leállítja az összes gorutint) csak csöveken vagy más szinkronizálási mechanizmusokon keresztül hajtható végre. A következő mintakód több gorutint indít el, és várja, hogy azok befejeződjenek a szinkronizálási rendszercsomag WaitGroup szinkronizálási objektumával. Ez az objektum tartalmaz egy számlálót, kezdetben nullát, amely növelheti és csökkentheti, és egy Wait() metódust, amely az aktuális szál szüneteltetését és megvárását, amíg a számláló nullára áll.

func main () { var wg sync . WaitGroup // Várakozócsoport létrehozása. A számláló kezdeti értéke 0 logger := log . New ( os . Stdout , "" , 0 ) // log.Logger egy szálbiztos kimeneti típus a _ , arg := tartomány os számára . Args { // Végigfut az összes parancssori argumentum wg . Add ( 1 ) // Növelje eggyel a várakozási csoport számlálóját // Futtasson le egy gorutint az arg paraméter feldolgozásához go func ( word string ) { // A várakozási csoport számlálójának késleltetett csökkentése eggyel. // Akkor történik, amikor a függvény véget ér. defer wg . Kész () logger . Println ( előkészítiWord ( szó )) // Feldolgozás végrehajtása és az eredmény kinyomtatása }( arg ) } wg . Várjon () // Várjon, amíg a wg várócsoport számlálója nulla lesz. }

Itt minden új gorutin létrehozása előtt a wg objektum számlálója eggyel növekszik, a gorutin befejezése után pedig eggyel csökken. Ennek eredményeként az argumentumok feldolgozását indító ciklusban annyi egység kerül a számlálóba, ahány gorutin indul. Amikor a ciklus véget ér, a wg.Wait() meghívása a főprogram szüneteltetését okozza. Ahogy mindegyik gorutin befejeződik, eggyel csökkenti a wg számlálót, így a fő program várakozása akkor ér véget, amikor annyi gorutin befejeződik, amennyi futott. Az utolsó sor nélkül a főprogram az összes gorutin futtatása után azonnal kilép, megszakítva azok végrehajtását, amelyeknek nem volt ideje végrehajtani.

A nyelvbe épített többszálú feldolgozás ellenére nem minden szabványos nyelvi objektum szálbiztos. Tehát a szabványos térképtípus (megjelenítés) nem szálbiztos. A nyelv megalkotói ezt a döntést hatékonysági megfontolásokkal magyarázták, mivel az összes ilyen objektum biztonságának biztosítása plusz többletköltséggel járna, ami messze nem mindig szükséges (ugyanezek a leképezésekkel végzett műveletek részei lehetnek nagyobb, a programozó által már szinkronizált műveleteknek is. , és akkor a további szinkronizálás csak bonyolítja és lelassítja a programot). Az 1.9-es verziótól kezdődően a párhuzamos feldolgozást támogató szinkronkönyvtár-csomag hozzáadta a szálbiztos sync.Map típust, amely szükség esetén használható. Figyelembe veheti a példában használt szálbiztos típust is az eredmények megjelenítéséhez log.Logger; a szabványos fmt csomag helyett használatos, amelynek funkciói (Printf, Println és így tovább) nem szálbiztosak, és további szinkronizálást igényelnének.

Objektumorientált programozás

A Go-ban nincs speciális kulcsszó az osztály deklarálására, de a metódusok bármely elnevezett típushoz definiálhatók, beleértve a struktúrákat és az alaptípusokat, például az int , tehát OOP értelemben minden ilyen típus osztály.

írja be a newInt int

A metódusdefiníciós szintaxis az Oberon-2 nyelvből kölcsönzött, és abban különbözik a szokásos függvénydefiníciótól, hogy a func kulcsszó után zárójelben van deklarálva az úgynevezett „receiver” ( angolul  Receiver ) , vagyis az az objektum, amelyre a metódus hívódik meg, és a típus, amelyhez a metódus tartozik. Míg a hagyományos objektumnyelvekben a vevő hallgatólagos és szabványos névvel rendelkezik (C++-ban vagy Java-ban "ez", ObjectPascalban "self" stb.), a Go nyelvben kifejezetten meg van adva, és a neve is megadható. bármely érvényes Go azonosító .

type myType struct { i int } // Itt p a vevő a myType típusú metódusokban. func ( p * myType ) get () int { return p . i } func ( p * myType ) set ( i int ) { p . én = én }

A Go-ban nincs formális osztályok (struktúrák) öröklődése, de van egy technikailag szoros beágyazási mechanizmus .  A szerkezet leírásában használhatjuk az úgynevezett anonim mezőt - olyan mezőt, amelyhez nincs megadva név, csak típus. Egy ilyen leírás eredményeként a beágyazott szerkezet minden eleme a beágyazott szerkezet azonos nevű elemévé válik.

// Új struktúra típus típusa myType2 struct { myType // Az Anonymous mező a myType típusú beágyazását biztosítja. // Most a myType2 tartalmazza az i mezőt és a get() és set(int) metódusokat. k int }

A klasszikus öröklődéstől eltérően a beágyazás nem jár polimorf viselkedéssel (egy beágyazó osztály objektuma nem működhet egy beágyazható osztály objektumaként kifejezett típuskonverzió nélkül).

Egy névtelen típushoz nem lehet kifejezetten metódusokat deklarálni (a szintaxis egyszerűen nem teszi lehetővé a vevő típusának megadását a metódusban), de ez a korlátozás könnyen megkerülhető, ha a megnevezett típust beillesztjük a szükséges metódusokkal.

Az osztálypolimorfizmust a Go-ban az interfészek mechanizmusa biztosítja (hasonlóan a C++ teljesen absztrakt osztályaihoz ). Egy interfész leírása az interfész kulcsszó használatával történik, a belső leírások (az osztály típusú deklarációkkal ellentétben) deklarálják az interfész által biztosított metódusokat.

írja be a myInterface interface { get () int set ( i int ) }

A Go-ban nincs szükség kifejezetten kijelenteni, hogy egy típus egy adott interfészt valósít meg. Ehelyett a szabály az, hogy minden típus, amely egy interfészben definiált metódusokat biztosít, használható az adott interfész megvalósításaként. A fent deklarált típus myTypeaz interfészt valósítja meg myInterface, bár ez sehol nincs kifejezetten feltüntetve, mert olyan get()és metódusokat tartalmaz set(), amelyek aláírása megegyezik a -ban leírtakkal myInterface.

Az osztályokhoz hasonlóan az interfészek is beépíthetők:

írja be a mySecondInterface interface { myInterface // ugyanaz, mint explicit deklarálja get() int; set(i int) change ( i int ) int }

Itt a mySecondInterface interfész örökli a myInterface interfészt (azaz deklarálja, hogy felfedi a myInterface-ben szereplő metódusokat), és emellett deklarál egy natív metódust change().

Noha elvileg lehetséges a Go programot az interfészek hierarchiájába építeni, ahogyan más objektumnyelveknél teszik, és még az öröklődést is szimulálni, ez rossz gyakorlatnak minősül. A nyelv nem hierarchikus, hanem kompozíciós megközelítést diktál az osztályok és interfészek rendszeréhez. A struktúraosztályok ezzel a megközelítéssel általában formálisan függetlenek maradhatnak, és az interfészeket nem egyesítik egyetlen hierarchiába, hanem adott alkalmazásokhoz hozzák létre, szükség esetén a meglévőket beágyazva. Az interfészek implicit megvalósítása a Go-ban rendkívüli rugalmasságot és minimális technikai nehézséget biztosít ezeknek a mechanizmusoknak.

Az öröklődésnek ez a megközelítése összhangban van a modern programozás néhány gyakorlati irányzatával. Tehát a híres "négyes banda" ( Erich Gamma és mások) című könyvben a tervezési mintákról különösen ez van írva:

A megvalósítási függőség problémákat okozhat egy alosztály újrafelhasználásakor. Ha a legacy implementáció egyetlen aspektusa is alkalmatlan az új tartományhoz, akkor a szülőosztályt át kell írni vagy valami megfelelőbbre kell cserélni. Ez a függőség korlátozza a rugalmasságot és az újrafelhasználhatóságot. A probléma csak absztrakt osztályokból való örökléssel oldható meg, mivel ezeknek általában nincs vagy csak minimális implementációjuk van.

A Go-ban nincs virtuális függvény fogalma . A polimorfizmust interfészek biztosítják. Ha egy metódus meghívására egy közönséges típusú változót használunk, akkor az ilyen hívás statikusan kötött, vagyis mindig az adott típushoz meghatározott metódus hívódik meg. Ha a metódust egy „interfész” típusú változóhoz hívjuk, akkor az ilyen hívás dinamikusan kötődik, és a végrehajtáskor az a metódusváltozat, amely a hívásakor ténylegesen hozzárendelt objektum típusához van definiálva. változó van kiválasztva indításhoz.

A Go objektumorientált programozásának dinamikus támogatását a GOOP projekt biztosítja .

Reflexió

A futás közbeni introspektív képesség, azaz bármilyen típusú érték elérése és feldolgozása, valamint a feldolgozott adattípusok dinamikus igazítása a Go rendszercsomag segítségével valósul meg reflect. Ez a csomag lehetővé teszi, hogy:

  • meghatározza bármely érték típusát;
  • hasonlítson össze bármely két értéket az egyenértékűség érdekében, beleértve azokat is, amelyeket nem hasonlítanak össze szabványos nyelvi eszközök, például szeletek;
  • dolgozzon ugyanazzal a kóddal bármilyen típusú értékkel (egy típus reflect.Valuelehetővé teszi bármely nyelvtípus értékének megjelenítését és átalakítását a szabványos típusok egyikére, ha lehetséges);
  • változtasson meg bármilyen értéket, ha ez elvileg lehetséges (például egy karakterlánc egy részének megváltoztatása);
  • típusok feltárása, különösen hozzáférési struktúra mezők és leíróik, típusmetódusok listája, leírásaik lekérése;
  • tetszőleges függvények és metódusok meghívása.

A csomag reflectszámos segédeszközt is tartalmaz a program dinamikus állapotától függő műveletek végrehajtásához.

Alacsony szintű programozás

Az alacsony szintű memória-hozzáférési lehetőségek a rendszercsomagban koncentrálódnak unsafe. Különlegessége, hogy bár úgy néz ki, mint egy normál Go csomag, valójában maga a fordító implementálja. A csomag unsafehozzáférést biztosít az adatok belső reprezentációjához és a "valódi" memóriamutatókhoz. A következő tulajdonságokkal rendelkezik:

  • unsafe.Sizeof() - az argumentum bármilyen típusú kifejezés lehet, a függvény az operandus tényleges méretét adja vissza bájtban, beleértve a fel nem használt memóriát is, amely az igazítás miatt megjelenhet a struktúrákban;
  • unsafe.Alignof() - az argumentum tetszőleges típusú kifejezés lehet, a függvény a méretet adja vissza bájtban, amely szerint az operandus típusai sorba rendeződnek a memóriában;
  • unsafe.Offsetof() - az argumentumnak a struktúra mezőjének kell lennie, a függvény azt az eltolást adja vissza bájtban, amelynél ez a mező található a struktúrában.

A csomag tartalmaz egy olyan típust is unsafe.Pointer, amely bármilyen mutatóvá konvertálható, és bármilyen típusú mutatóvá konvertálható, valamint egy szabványos típust is uintptr , egy előjel nélküli egész értéket, amely elég nagy ahhoz, hogy egy teljes címet tároljon az aktuális platformon. A mutatót -ra, unsafe.Pointermajd -ra konvertálva uintptregész számként kaphatjuk meg a címet, amelyre aritmetikai műveleteket alkalmazhatunk. Ha ezután visszakonvertálja az értéket unsafe.Pointeregy adott típusú mutatóvá, akkor szinte bárhol elérheti a címteret ilyen módon.

A leírt átalakítások nem biztonságosak lehetnek, ezért lehetőleg kerülni kell őket. Először is nyilvánvaló problémák vannak a rossz memóriaterülethez való hibás hozzáféréssel kapcsolatban. Egy finomabb dolog az, hogy a csomag használata ellenére a unsafeGo objektumokat továbbra is a memóriakezelő és a szemétgyűjtő kezeli. Ha egy mutatót számmá alakítunk, a mutató kikerül az ellenőrzés alól, és a programozó nem számíthat arra, hogy egy ilyen átalakított mutató a végtelenségig releváns marad. Például megpróbál egy mutatót tárolni egy új típusú objektumra, például Т:

pT := uintptr ( nem biztonságos . Mutató ( new ( T ))) // HIBA!

létrehozza az objektumot, a mutatót pedig számmá alakítja (amely a következőhöz lesz hozzárendelve pT). Ennek azonban pTegész típusa van, és a szemétgyűjtő nem tekinti egy létrehozott objektum mutatójának, így a művelet befejezése után a memóriakezelő rendszer ezt az objektumot nem használja fel. Vagyis a szemétgyűjtő eltávolíthatja, utána az átalakított mutató pTérvénytelenné válik. Ez bármikor megtörténhet, mind közvetlenül a művelet befejezése után, mind a program több órás működése után, így a hiba véletlenszerű programösszeomlásokban fejeződik ki, amelyek okát rendkívül nehéz lesz azonosítani. Mozgó szemétgyűjtő [* 1] használatakor pedig a számmá konvertált mutató irrelevánssá válhat, még akkor is, ha az objektumot még nem távolították el a memóriából.

Mivel a Go specifikáció nem ad pontos jelzéseket arra vonatkozóan, hogy a programozó milyen mértékben számíthat arra, hogy egy számmá konvertált mutatót naprakészen tartson, van egy javaslat: az ilyen átalakításokat minimálisra kell csökkenteni, és úgy kell megszervezni őket, hogy az eredeti mutató, annak módosításai és visszakonverziója egy nyelvi utasításon belül van, és ha olyan könyvtári függvényeket hívunk meg, amelyek formátumban adnak vissza címet uintptr, azonnal konvertálják az eredményüket a mutatóba, unsafe.Pointerhogy megőrizzék a garancia elvesztését.

A csomagot unsaferitkán használják közvetlenül az alkalmazásprogramozásban, de aktívan használják a reflect, os, syscall, context, netés néhány más csomagban.

Interfész más nyelvű kóddal

Számos külső eszköz létezik, amelyek idegen funkciójú interfészt (FFI) biztosítanak a Go programok számára. A cgo segédprogram használható külső C kóddal való interakcióra (vagy C-kompatibilis interfésszel) . Ez automatikusan meghívódik, amikor a fordító feldolgoz egy megfelelően megírt Go modult, és biztosítja egy ideiglenes Go wrapper csomag létrehozását, amely tartalmazza az összes szükséges típus és funkció deklarációit. A C függvényhívásoknál gyakran csomaglehetőségeket kell igénybe vennie , főleg a . Erősebb eszköz a SWIG [16] , amely fejlettebb funkciókat biztosít, mint például a C++ osztályokkal való integráció . unsafeunsafe.Pointer

Felhasználói felület

A Go szabványos könyvtára támogatja a konzolalkalmazások és a webalapú szerveralkalmazások építését , de nincsenek szabványos eszközök a grafikus felhasználói felületek kliens alkalmazásokban történő létrehozásához. Ezt a hiányt a népszerű felhasználói felületi keretrendszerek , például a GTK+ és a Qt harmadik féltől származó burkolói pótolják , Windows alatt a WinAPI grafikus eszközöket is használhatja , ha a csomagon keresztül éri el őket syscall, de mindezek a módszerek meglehetősen körülményesek. Magában a Go-ban is számos UI keretrendszer fejlesztés létezik, de egyik projekt sem érte el az ipari alkalmazhatóság szintjét. 2015-ben a denveri GopherCon konferencián a nyelv egyik megalkotója, Robert Grismer válaszolt a kérdésekre, egyetértett azzal, hogy a Go-nak szüksége van felhasználói felületi csomagra, de megjegyezte, hogy egy ilyen csomagnak univerzálisnak, erőteljesnek és többplatformosnak kell lennie. fejlesztés hosszú és nehéz folyamat. A kliens GUI megvalósításának kérdése továbbra is nyitott.

Kritika

A nyelv fiatalsága miatt kritikája elsősorban internetes cikkekben, ismertetőkben, fórumokon összpontosul.

Lehetőségek hiánya

A nyelvvel kapcsolatos kritikák nagy része arra irányul, hogy hiányoznak a más nyelvek által biztosított népszerű jellemzők. Köztük [17] [18] [19] [20] :

Mint fentebb említettük, számos más népszerű nyelven elérhető funkció hiánya a fejlesztők tudatos választásának köszönhető, akik úgy vélik, hogy az ilyen szolgáltatások vagy akadályozzák a hatékony fordítást, vagy hibázásra késztetik a programozót, vagy nem hatékony, ill. "rossz" a kód karbantartása szempontjából, vagy egyéb nemkívánatos mellékhatásai vannak.

Építészet

  • Nincsenek felsorolt ​​típusok. Ehelyett konstanscsoportokat használunk, de minden értékállandó valójában egész szám, ezek a csoportok szintaktikailag nincsenek kombinálva, és a fordítónak nincs befolyása a használatukra. Nem lehet leírni egy felsoroláshoz társított típust, például egy tömböt, amely a felsorolás minden eleméhez tartalmaz egy elemet (amit Pascalban a fajta konstrukció ír le type EnumArray = array[EnumType] of ElementType), hurkot hoz létre a felsorolás felett, a fordító nem tudja szabályozni az alternatívák listájának teljessége a konstrukcióban switch, ha az értéket választó felsorolásként használjuk.
  • A beépített tároló adattípusok hiánya.
    A nyelvbe épített tárolók tömbökre és leképezésekre korlátozódnak. A magával a nyelvvel megvalósított konténerek (beleértve a szabványos könyvtárban lévőket is) a bennük lévő típusú elemek kényszerhasználata miatt nem típusbiztonságosakinterface{} , ráadásul nem is megkerülhetők a for range.
  • Az interfész típusonkénti megvalósítására vonatkozó kifejezett jelzés hiánya megnehezíti a kód megértését, módosítását és újrafeldolgozását . A fordító nem tudja automatikusan ellenőrizni a típust a megvalósított interfészekkel. Lehetséges (bár valószínűtlen) olyan interfész "véletlenül implementálása", ahol egy típus metódusai ugyanazokkal az aláírásokkal rendelkeznek, mint az interfész metódusai, de lényegében nem az interfész által képviselt viselkedés megvalósítása.
  • A strukturális kivételkezelés elhagyása a visszatérő hibák helyett lehetetlenné teszi a hibakezelés egy helyre történő koncentrálását, a hibaellenőrzések összezavarják a kódot és megnehezítik a megértést. Ezenkívül a pánikállapot kezelésének mechanizmusa lényegében nem különbözik a kivételkezelőktől a try-catch. Sőt, saját ajánlásaikkal ellentétben a nyelv szerzői pánikgenerálást és feldolgozást alkalmaznak a logikai hibák kezelésére a szabványos könyvtáron belül.
  • A struktúramező-címkéket nem a fordító vezérli.
    A szerkezetmezők további tulajdonságait beállító címkék csak dinamikusan feldolgozott karakterláncok, formátumukra a legegyszerűbb szintaktikai megkötések sincsenek. Ez azért történik, hogy ne korlátozza a fejlesztőt a címkék használatában, de a gyakorlatban ez oda vezet, hogy a fordítási szakaszban nem észlelhető hiba a címke írásában.

"Butkók" (egyes eszközök sikertelen megvalósítása)

A kritikusok rámutatnak arra, hogy a Go egyes funkcióit a legegyszerűbb vagy leghatékonyabb implementáció szerint valósítják meg, de nem felelnek meg a " legkisebb meglepetés elvének ": viselkedésük eltér attól, amit a programozó az intuíció és a múltbeli tapasztalatok alapján elvárna. Az ilyen funkciók fokozott figyelmet igényelnek a programozótól, megnehezítik a tanulást és a más nyelvekről való váltást.

  • A gyűjtési ciklusban az értékváltozó másolat, nem hivatkozás.
    Az olyan ciklusban, mint a " for index, value := range collection", a változó valueaz aktuális elem másolata. A változóhoz új értéket rendelő művelet elérhető, de a várakozásokkal ellentétben nem változtat a gyűjtemény aktuális elemén.
  • A null interfész nem egyenlő a null objektum interfészével.
    Az "interfész" típusú érték két hivatkozásból álló szerkezet - a metódustáblára és magára az objektumra. A null interfészen mindkét mező egyenlő nil. A null objektumra mutató interfész első hivatkozása ki van töltve; nem egyenlő a null interfésszel, bár programlogikailag általában nincs különbség a kettő között [21] . Ez váratlan hatásokhoz vezet, és megnehezíti az interfésztípusok értékeinek helyességének ellenőrzését:
I. típusú interfész { f () } type T struct {} func ( T ) f () { ... } // A T típus megvalósítja az I interfészt. main () { var t * T = nil // t egy null mutató a T beírására. var i I = t // Írjon nullmutatót T-re egy interfész változóba. ha i != nulla { // ! Meglepetés. Habár nullmutatót kaptam, i != nil i . f () // Ez a hívás megtörténik, és pánikot okoz. } ... } Bár az iobjektum nulla mutatója a változóba lett írva, maga az érték inem üres, és az összehasonlítás i != nilpozitív eredményt ad. Annak biztosításához, hogy egy interfészváltozó érvényes objektumra mutasson, tükrözést kell használnia, ami jelentősen bonyolítja a kódot: ha én != nulla && ! tükrözi . ValueOf ( i ). isnil () { ...
  • Inhomogén hozzárendelési szemantika szorosan kapcsolódó típusokon is.
    A beépített típusok és struktúrák érték szerint, az interfészek hivatkozással vannak hozzárendelve. A statikusan deklarált hosszúságú tömbök érték, a deklarált hosszúságú és megjelenítés nélküli tömbök hozzárendelése referencia szerint történik. Valójában egy típushoz tartozó hozzárendelési szemantika megválasztását az határozza meg, hogy ennek a típusnak az értékeit hogyan foglalják le a memóriában, vagyis a nyelvet a megvalósítás határozza meg.
  • A tömbökön és szeleteken végzett műveletek eltérő viselkedése különböző feltételek mellett.
    Például egy szabványos függvény append(), amely elemeket ad hozzá egy tömbhöz, létrehozhat és visszaadhat egy új tömböt, vagy hozzáfűzhet és visszaadhat egy meglévőt, attól függően, hogy van-e benne elég szabad hely az elemek hozzáadásához. Az első esetben az eredményül kapott tömb későbbi módosításai nem érintik az eredetit, a második esetben azok tükröződnek benne. Ez a viselkedés a másolási funkció folyamatos használatát kényszeríti ki copy().

Egyéb jellemzők

Gyakran kritizálják az automatikus pontosvessző mechanizmusát, amely miatt az utasítások, függvényhívások és listák írásának egyes formái hibássá válnak. Ezt a döntést kommentálva a nyelvi szöveg szerzői megjegyzik [9] , hogy a hivatalos eszköztárban egy kódformázó jelenlétével együtt gofmtegy meglehetősen merev kódolási szabvány rögzítéséhez vezetett a Go-ban. Aligha lehet olyan szabványt alkotni a kódíráshoz, amely mindenkinek megfelelne; egy olyan szolgáltatás nyelvi bevezetése, amely önmagában is ilyen mércét állít fel, egységesíti a programok megjelenését és kiküszöböli a formázásból adódó elvtelen konfliktusokat, ami pozitív tényező a szoftverek csoportos fejlesztése és karbantartása szempontjából.

Megoszlás és perspektívák

A Go népszerűsége az elmúlt években nőtt: 2014-ről 2020-ra a Go a 65. helyről a 11. helyre emelkedett a TIOBE -rangsorban, a 2020. augusztusi értékelési érték 1,43%. Egy dou.ua felmérés [22] eredményei szerint a Go nyelv 2018-ban a kilencedik lett a leggyakrabban használt nyelvek listáján és a hatodik azon nyelvek listáján, amelyeket a fejlesztők személyesen preferálnak.

A 2012-es első nyilvános kiadás óta a nyelv használata folyamatosan növekszik. A Go projekt honlapján közzétett, az ipari fejlesztésben nyelvet használó cégek listája több tucat nevet tartalmaz. A különféle célokra szolgáló könyvtárak nagy skálája halmozódott fel. A 2.0-s verzió megjelenését 2019-re tervezték, de a munka késett, és 2022 második felében még folyamatban van. megjelenése várható , beleértve a generikus kifejezéseket és a hibakezelést egyszerűsítő speciális szintaxist, amelyek hiánya az egyik leggyakoribb panasz a nyelv kritikusaitól .

Golangban fejlesztették ki a RoadRunner (Application server) webszervert , amely lehetővé teszi a webes alkalmazások számára, hogy a hagyományos 200 ms helyett 10-20 ms-os kérés-válasz sebességet érjenek el. Ezt a webszolgáltatást a tervek szerint olyan népszerű keretrendszerekbe építik be, mint például a Yii .

A C ++ mellett a Golangot mikroszolgáltatások fejlesztésére használják, ami lehetővé teszi a többprocesszoros platformok munkával való „betöltését”. A REST segítségével kommunikálhat egy mikroszolgáltatással , és a PHP nyelv kiválóan alkalmas erre.

A Spiral Framework fejlesztése PHP és Golang segítségével történt. [23]

Verziók

Számozási és verziókompatibilitási elvek

Magának a Go nyelvnek csak egy fő verziója létezik, az 1. verzió. A Go fejlesztői környezet verziói (fordító, eszközök és szabványos könyvtárak) vagy kétjegyű számozást kapnak ("<nyelvi verzió>.<főkiadás>"), vagy három számjegyű ("<nyelvi verzió>.< fő kiadás>. <kisebb kiadás>") a rendszerhez. Az új "kétjegyű" verzió kiadása automatikusan megszünteti az előző "kétjegyű" verzió támogatását. „Háromjegyű” verziók jelentek meg a jelentett hibák és biztonsági problémák javítására; az ilyen verziókban található biztonsági javítások hatással lehetnek az utolsó két „kétjegyű” verzióra [24] .

A szerzők kinyilvánították [25] azt a törekvést, hogy amennyire csak lehetséges, megőrizzék a visszafelé kompatibilitást a nyelv fő változatán belül. Ez azt jelenti, hogy a Go 2 megjelenése előtt szinte minden, a Go 1 környezetben létrehozott program megfelelően lefordítja a Go 1.x következő verzióit, és hiba nélkül fut. Kivételek lehetségesek, de kevés. A kiadások közötti bináris kompatibilitás azonban nem garantált, ezért a programot teljesen újra kell fordítani, amikor a Go egy későbbi kiadására váltunk.

Menj 1

2012 márciusa óta, a Go 1 bemutatása óta a következő főbb verziók jelentek meg:

  • go 1 - 2012. március 28. - Első hivatalos kiadás; könyvtárak javítva, szintaktikai változtatások történtek.
  • go 1.1 - 2013. május 13. - az egész szám nullával való osztása szintaktikai hibává vált, metódusértékek kerültek bevezetésre - metóduslezárások adott forrásértékkel, egyes esetekben opcionálissá vált a visszatérés használata; az implementáció választhat a szabványos egész típus 32 bites és 64 bites megjelenítése között, változtatások a Unicode támogatásban.
  • go 1.2 - 2013. december 1. - a nulla mutató elérési kísérlete garantáltan pánikot kelt, három indexes szeletek kerülnek bevezetésre. A Unicode fejlesztései.
  • go 1.3 - 2014. június 18. - megváltoztatta a memóriafoglalási modellt; eltávolították a Windows 2000 platform támogatását, hozzáadták a DragonFly BSD-t, a FreeBSD-t, a NetBSD-t, az OpenBSD-t, a Plan 9-et, a Solarist.
  • go 1.4 - 2014. december 10. - a ciklus felépítése az x { ... } tartományhoz megengedett (gyüjteményen keresztüli ciklus változók használata nélkül), metódus hívásakor tilos a dupla automatikus hivatkozás megszüntetése (ha x ** T egy dupla mutató a T beírására, majd az x metódusának meghívása xm() formában - tiltott); Android, ARM-en NaCl, AMD64 platformokon Plan9 támogatás került a megvalósításba.
  • go 1.5 - 2015. augusztus 19. - a térképi literálok jelölésénél az egyes elemek típusának feltüntetése nem kötelező, a megvalósításban a futási környezet és a fordító teljesen át van írva Go-ban és assemblerben, a C nyelv már nem használatos.
  • go 1.6 – 2016. február 17. – nincs nyelvi változás, a környezet Linuxra portolva 64 bites MIPS-en, Android 32 bites x86-on (android/386), eszköztár változások.
  • go 1.7 – 2016. augusztus 16. – Csökkentett fordítási idő és bináris fájlok mérete, megnövelt sebesség, és hozzáadtuk a környezeti csomagot a szabványos könyvtárhoz.
  • go 1.8 - 2017. április 7. - a beépített memóriaszemétgyűjtő felgyorsult, a "http" modul megkapta a soft stop képességét, hozzáadták a MIPS architektúrával (32 bites) processzorok támogatását. Számos csomagon és segédprogramon korrekcióra került sor.
  • go 1.9 - 2017. augusztus 24. - típusnevek álnevei kerültek a nyelvbe, tisztázták a lebegőpontos műveletek használatának egyes pontjait, optimalizálták az eszközöket, hozzáadtak könyvtárakat, különösen a szálbiztos térképtípust.
  • go 1.10 - 2018. február 16. - két pontosítás történt a nyelven, ami ténylegesen legitimálta a meglévő implementációkat, a többi változtatás a könyvtárakra és az eszközökre vonatkozik. Három "három számjegyű" 1.10.1 - 1.10.3 kiadás jelent meg, amelyek az észlelt hibák javításait tartalmazzák.
  • go 1.11 - 2018. augusztus 24. - hozzáadott (kísérleti) támogatást a modulokhoz (új csomagverziós mechanizmus és függőségek kezelése), valamint a WebAssembly -re való fordítás képessége , továbbfejlesztett támogatás az ARM processzorokhoz, változtatások történtek az eszköztárban és a könyvtárakban (különösen hozzáadva a syscall/js csomagot; a fordító most helyesen vezérli a switch utasításokban deklarált változók használatát típusellenőrzéssel).
  • go 1.12 – 2019. február 25. – javítások a könyvtárakban és a segédprogramokban. Az utolsó kiadásként jelentették be, amely megtartja a FreeBSD 10.X és a macOS 10.10 támogatását. Hozzáadott cgo támogatás a linux/ppc64 platformon. Hozzáadott támogatás az AIX OS számára . 2019 augusztusáig kilenc javítási kiadás jelent meg ennek a kiadásnak a részeként, amelyek különféle hibákat javítottak.
  • go 1.13 - 2019. szeptember 3. - új numerikus literálok kerültek a nyelvbe: bináris és oktális egész számok, hexadecimális lebegőpont (ez utóbbinak tartalmaznia kell a kitevőt, p vagy P szimbólummal elválasztva); lehetővé tette az aláhúzás használatát a számjegyek elválasztására; a bitenkénti eltolás művelet megengedett előjeles egész számok esetén; hozzáadott támogatás az Android 10-hez; a régebbi verziók támogatása számos platformon megszűnt.
  • go 1.14 – 2020. február 25. – Az interfészek belefoglalásának definíciója kibővült: mostantól több olyan interfész is szerepelhet, amelyek azonos nevű metódusaival azonos aláírással rendelkeznek. Változások a könyvtárakban, a futtatókörnyezetben, az eszközökben.
  • go 1.15 – 2020. augusztus 11. – A Darwin kernel 32 bites operációs rendszer-változatainak támogatása megszűnt, a linker teljesítménye jobb, opcionális Spectre sebezhetőség -csökkentési lehetőség , új Go vet eszközzel kapcsolatos figyelmeztetések. Ebben a kiadásban nem történt nyelvi változás. 2020 novemberének végéig öt kisebb kiadás jelent meg, amelyek javították a hibákat és a biztonsági réseket.
  • go 1.16 – 2021. február 16. – Hozzáadott támogatás a 64 bites ARM-hez macOS és NetBSD alatt, a MIPS64-hez OpenBSD alatt, továbbfejlesztett megvalósítás számos architektúrához, beleértve a RISC-V-t is. A modulok támogatása alapértelmezés szerint engedélyezve van, a build parancs paramétereihez hozzáadták a verziók explicit megadásának lehetőségét. Nincsenek nyelvi változások. Változások történtek a könyvtárakban, különösen egy olyan csomag került be embed, amely megvalósítja a futtatható modulba épített fájlok elérését. 2021 júniusáig öt kisebb kiadás jelent meg.

Go 2.0

Fejlesztési haladás 2017 óta folynak az előkészületek a nyelv következő alapváltozatának kiadására, amely a „Go 2.0” szimbólumot viseli [26] . A projekt wiki oldalán felhalmozott, az aktuális verzióhoz fűzött megjegyzések és átalakítási javaslatok gyűjteménye [27] . Kezdetben azt feltételezték, hogy az előkészítési folyamat "körülbelül két évig" tart majd, és a nyelv néhány új eleme bekerül a Go 1 verzió következő kiadásaiba (természetesen csak azok, amelyek nem sértik a visszafelé kompatibilitást ). [26] 2021 áprilisára a 2.0-s verzió még nem készült el, a tervezett változtatások egy része a tervezési és megvalósítási szakaszban van. A projektblogban [28] vázolt tervek szerint legalább 2021-ig folytatódik a munka a tervezett változtatások megvalósításán. Javasolt újítások Az alapvető újítások közé tartoznak a kifejezetten deklarált konstans értékek, az új hibakezelési mechanizmus és az általános programozási eszközök. Az innovációs projektek online elérhetők. 2018. augusztus 28-án a hivatalos fejlesztői blogon megjelent egy korábban a Gophercon 2018 konferencián bemutatott videó , amely bemutatja az új hibakezelési tervezés és az általános funkciók mechanizmusának vázlatos változatait. Sok kevésbé észrevehető, de nagyon jelentős változtatást is terveznek [29] , mint például a karakterek megengedettségére vonatkozó szabályok kiterjesztése a nem latin ábécé azonosítóihoz, lehetővé válik az előjeles egész számok eltolása, az aláhúzás használata az ezres csoportok elválasztójaként. számokban, bináris literálokban . A legtöbbjük már implementálva van, és elérhető a Go 1 legújabb verziójában. Hiba a feldolgozásban A hibakezelési mechanizmus módosítására több lehetőséget is mérlegeltek, különösen egy külön hibakezelővel rendelkező tervezést (" Hibakezelés - Tervezettervezet "). A 2019. júliusi utolsó változatot az „ Javaslat: Beépített Go hibaellenőrző funkció, próbálkozzon ” című cikk ismerteti. Ez a lehetőség a legminimálisabb, és csak egy olyan beépített függvény hozzáadását jelenti, try()amely feldolgozza a függvényhívás eredményét. Használatát az alábbi pszeudokód szemlélteti. func f ( )( r1 type_1 , , rn type_n , err error ) { // Tesztelt függvény // n+1 eredményt ad vissza: r1... rn, err típusú hiba. } func g ( )( , err error ) { // Az f() függvény hívása hibaellenőrzéssel: x1 , x2 , xn = try ( f ( )) // A beépített try használata: // if f( ) nem nullát adott vissza az utolsó eredményben, akkor a g() automatikusan befejeződik, // ugyanazt az értéket adja vissza az ITS utolsó eredményében. } func t ( )( , err error ) { // Hasonló a g()-hez az új szintaxis használata nélkül: t1 , t2 , tn , te := f ( ) // Az f() meghívása ideiglenesen tárolt eredményekkel változók. if te != nil { // Ellenőrizze a visszatérési kódot a nil egyenlőséghez azonnal. } // Ha nem volt hiba, akkor x1 , x2 , xn = t1 , t2 , tn // … x1…xn változók megkapják az értéket // és a t() végrehajtása folytatódik. } Vagyis try()egyszerűen hibaellenőrzést biztosít az ellenőrzött függvény hívásában, és azonnali visszatérést az aktuális függvényből ugyanazzal a hibával. A mechanizmus segítségével kezelheti a hibát, mielőtt visszatérne az aktuális függvényből defer. A használat try()megköveteli, hogy mind az ellenőrzött, mind a meghívott függvénynek az utolsó típusú visszatérési értékkel kell rendelkeznie error. Ezért például nem main()használhatja a try(); a legfelső szinten minden hibát kifejezetten kezelni kell. Ennek a hibakezelési mechanizmusnak a Go 1.14 -ben kellett volna szerepelnie , de ez nem történt meg. A megvalósítás dátumai nincsenek megadva. Általános kód 2018 végén bemutatták az általános típusok és funkciók Go-ban való megvalósításának tervezetét [30] . 2020. szeptember 9-én megjelent egy átdolgozott terv [31] , amelyben a függvények, típusok és függvényparaméterek paramétertípusokkal paraméterezhetők , amelyeket viszont megszorítások vezérelnek . // A Stringer egy megszorító interfész, amelyhez a típus szükséges // egy String metódus megvalósításához, amely egy karakterlánc értéket ad vissza. type Stringer interface { String () string } // A függvény bemenetként kap egy tetszőleges típusú értéktömböt, amely megvalósítja a String metódust, és // visszaadja a megfelelő karakterlánc-tömböt, amelyet a String metódus meghívásával kapunk a bemeneti tömb minden eleméhez. func Stringify [ T Stringer ] ( s [] T ) [] string { // a T típusparaméter, a Stringer kényszertől függően, // az s tömbparaméter értéktípusa. ret = make ([] string , len ( s )) for i , v := range s { ret [ i ] = v . String () } return ret } ... v := make ([] MyType ) ... // Általános függvény hívásához meg kell adni egy konkrét típust s := Stringify [ String ]( v ) Itt a függvény Stringifyegy típusparamétert tartalmaz T, amelyet egy normál paraméter leírásában használunk s. Egy ilyen függvény meghívásához, amint az a példában is látható, meg kell adni a hívásban azt a konkrét típust, amelyre hívva van. StringerEbben a leírásban ez egy olyan megszorítás , amely megköveteli a MyType-tól, hogy olyan Stringparaméter nélküli metódust valósítson meg, amely karakterlánc-értéket ad vissza. Ez lehetővé teszi a fordító számára a " " kifejezés helyes feldolgozását v.String(). Az általános kód bevezetését a 2021 augusztusára tervezett 1.18-as verzióban jelentették be. [28]

Megvalósítások

Jelenleg két fő Go fordító létezik:

  • A gc  a nyelvfejlesztő csapat által karbantartott hivatalos fejlesztői eszközök általános neve. Eredetileg tartalmazta a 6g (amd64-hez), 8g (x86-hoz), 5g (ARM-hez) fordítókat és a kapcsolódó eszközöket, és C nyelven íródott a yacc / Bison használatával az elemzőhöz. Az 1.5-ös verzióban az összes C-kódot átírták a Go és az assembler programban, és az egyes fordítókat egyetlen go tool-fordítással helyettesítették .
Támogatott: FreeBSD , OpenBSD , Linux , macOS , Windows , DragonFly BSD , Plan 9 , Solaris , Android , AIX : bináris disztribúciók állnak rendelkezésre a FreeBSD , Linux , macOS , Windows jelenlegi verzióihoz , más platformokhoz forrásból való fordítás szükséges. A fejlesztők a platformverziók korlátozott listáját támogatják a fordító új kiadásaiban, kizárva a támogatott verziók listájából, amelyek a kiadás időpontjában elavultnak minősülnek. Például a gc 1.12 támogatja a Windows 7 és a Server 2008R vagy újabb verzióit.
  • A gccgo  egy Go fordító C++ nyelven írt kliensoldallal és rekurzív értelmezővel, amely a szabványos GCC háttérrel kombinálva [32] . A Go támogatás a 4.6-os verzió óta elérhető a GCC-ben [33] . A gc fordítóval kapcsolatos eltérések többsége a futásidejű könyvtárhoz kapcsolódik, és nem látható a Go programok számára. [34] A gcc 8.1-es kiadása támogatja az összes nyelvi változtatást az 1.10.1-es verzióig, és integrálja a párhuzamos szemétgyűjtőt. [35] A szálak (go-procedures) a gccgo-ban OS szálakon keresztül valósulnak meg, aminek következtében az aktívan párhuzamos számítást használó programok lényegesen magasabb rezsiköltségekhez vezethetnek. A könnyű streamek támogatása lehetséges az arany linkerrel, de nem minden platformon érhető el.

Vannak projektek is:

  • Az llgo  egy réteg a Go into llvm fordítására , magában a go-ban íródott (2014-ig volt fejlesztés alatt) [36] [37] .
  • A gollvm a Google által kifejlesztett  Go through the LLVM fordítórendszer fordítási projektje. Használja a C++ elemző "gofrontend"-et a GCCGO-tól és konvertálja a gofrontend nézetből LLVM IR-be [38] [39]
  • Az SSA interpreter  egy olyan tolmács, amely lehetővé teszi go programok futtatását [40] .
  • A TinyGo egy Go fordító, amelynek célja kompakt végrehajtható fájlok létrehozása mikrokontrollerekhez és WebAssembly-hez LLVM használatával .

Fejlesztő eszközök

A Go fejlesztői környezet számos parancssori eszközt tartalmaz: a fordítást, tesztelést és csomagkezelést biztosító go segédprogramot, valamint a godoc és gofmt segédprogramokat, amelyek a programok dokumentálására és a forráskód szabványos szabályok szerinti formázására szolgálnak. Az eszközök teljes listájának megjelenítéséhez argumentumok megadása nélkül kell meghívnia a go segédprogramot. A gdb hibakereső használható programok hibakeresésére. A független fejlesztők számos eszközt és könyvtárat kínálnak a fejlesztési folyamat támogatására, főként a kódelemzés, tesztelés és hibakeresés megkönnyítésére.

Jelenleg két IDE érhető el, amelyek eredetileg a Go nyelvre összpontosítanak – ez a szabadalmaztatott GoLand [1] (amelyet a JetBrains fejlesztett ki az IntelliJ platformon) és az ingyenes LiteIDE [2] (korábban a projekt neve GoLangIDE). A LiteIDE egy kis shell, amelyet C++ nyelven írnak Qt használatával . Lehetővé teszi a fordítást, a hibakeresést, a kód formázását és az eszközök futtatását. A szerkesztő támogatja a szintaxis kiemelését és az automatikus kiegészítést.

A Go-t az univerzális Eclipse, NetBeans, IntelliJ, Komodo, CodeBox IDE, Visual Studio, Zeus és mások bővítményei is támogatják. Az automatikus kiemelés, a Go kód automatikus kiegészítése, valamint a fordító és kódfeldolgozó segédprogramok futtatása több mint kéttucatnyi általános szövegszerkesztő bővítményeként valósul meg különféle platformokon, köztük az Emacs, Vim, Notepad++, jEdit.

Példák

Az alábbiakban egy példa látható a "Hello, World!" a Go nyelven.

csomag import "fmt" func main () { fmt . println ( "Helló, világ!" ) }

Egy példa a Unix echo parancs megvalósítására :

csomag import ( "os" "flag" // parancssori elemző ) var omitNewLine = flag . Bool ( "n" , false , "ne nyomtatjon újsort" ) const ( Space = " " NewLine = "\n" ) func main () { flag . Elemzés () // Vizsgálja meg az argumentumlistát, és állítsa be a flags var s stringet i : = 0 esetén ; i < zászló . Narg (); i ++ { if i > 0 { s += Space } s += flag . Arg ( i ) } ha ! * omitNewLine { s += NewLine } os . Stdout . WriteString ( ek ) }

Jegyzetek

Hozzászólások
  1. 2019-től a Go egyetlen implementációja sem használ mozgó szemétgyűjtőt.
Források
  1. A Go egy objektum-orientált nyelv? . - "Bár a Go-nak vannak típusai és metódusai, és lehetővé teszi az objektum-orientált programozási stílust, nincs típushierarchia." Letöltve: 2019. április 13. Az eredetiből archiválva : 2020. május 3.
  2. Go: kegyelettel növekvő kód . - "A Go objektum orientált, de nem a szokásos módon." Letöltve: 2018. június 24. Az eredetiből archiválva : 2022. június 18.
  3. https://go.dev/doc/devel/release#go1.19.minor
  4. 1 2 3 https://golang.org/doc/faq#ancestors
  5. https://talks.golang.org/2015/gophercon-goevolution.slide#19 – 2015.
  6. 1 2 http://golang.org/doc/go_faq.html#ancestors
  7. https://talks.golang.org/2014/hellogophers.slide#21
  8. Google-go-nyelv . Letöltve: 2017. szeptember 28. Az eredetiből archiválva : 2010. január 18..
  9. 1 2 3 4 5 6 Nyelvi tervezés GYIK . Letöltve: 2013. november 11. Az eredetiből archiválva : 2019. január 7..
  10. Első lépések – A Go programozási nyelv . Letöltve: 2009. november 11. Az eredetiből archiválva : 2012. március 20.
  11. 1 2 Névütközés jelentése a hibakövetőben . Letöltve: 2017. október 19. Az eredetiből archiválva : 2018. február 23.
  12. 1 2 3 Ugrás a Google oldalára: Nyelvi tervezés a szoftverfejlesztés szolgálatában . talks.golang.org. Letöltve: 2017. szeptember 19. Az eredetiből archiválva : 2021. január 25.
  13. Rob Pike. A Go programozási nyelv. golang.org, 2009.10.30. . Letöltve: 2018. november 3. Az eredetiből archiválva : 2017. augusztus 29.
  14. amikor m[-1]a tömb utolsó elemét jelenti, akkor m[-2] a végétől számítva a második, és így tovább
  15. Andrew Gerrand. Halasztás, pánik és helyreállítás a GoBlogon . Letöltve: 2016. március 19. Az eredetiből archiválva : 2014. április 20..
  16. SWIG . Letöltve: 2018. november 27. Az eredetiből archiválva : 2018. november 28.
  17. Yager, a Will Why Go nem jó . Letöltve: 2018. november 4. Az eredetiből archiválva : 2019. július 16.
  18. Elbre, Egon A Go Generics-beszélgetések összefoglalása . Letöltve: 2018. november 4. Az eredetiből archiválva : 2019. július 15.
  19. Dobronszki János Mindennapi gondok a Go-ban . Letöltve: 2018. november 4. Az eredetiből archiválva : 2019. április 10.
  20. Fitzpatrick, Brad Go: 90%-ban tökéletes, az esetek 100%-ában . Letöltve: 2016. január 28. Az eredetiből archiválva : 2019. február 3..
  21. Donovan, 2016 , p. 224-225.
  22. Programozási nyelvek rangsorolása 2018: A Go és a TypeScript bekerült a nagy ligákba, Kotlint komolyan kell venni  (orosz) , DOW . Archiválva az eredetiből: 2020. augusztus 4. Letöltve: 2018. július 29.
  23. Spirális keretrendszer . Letöltve: 2020. május 23. Az eredetiből archiválva : 2020. május 13.
  24. https://golang.org/doc/devel/release.html Archivált 2017. február 17-én a Wayback Machine Go verzióban.
  25. https://golang.org/doc/go1compat Archiválva : 2017. október 2. a Wayback Machine Go 1-nél és a Go jövőbeli kiadásainál.
  26. 1 2 Toward Go 2 - The Go Blog . blog.golang.org. Letöltve: 2018. július 29. Az eredetiből archiválva : 2018. június 26.
  27. golang/  go . GitHub. Letöltve: 2018. július 29. Az eredetiből archiválva : 2018. augusztus 29.
  28. 1 2 Russ Cox, "Eleven Years of Go" . Letöltve: 2020. november 26. Az eredetiből archiválva : 2020. november 27.
  29. Go2 Itt vagyunk! . Letöltve: 2018. december 6. Az eredetiből archiválva : 2018. december 1..
  30. ↑ Szerződések Tervezés  . go.googlesource.com. Letöltve: 2018. október 11. Az eredetiből archiválva : 2018. október 11.
  31. https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md Archiválva : 2020. június 23. a Wayback Machine Type Parameters - Tervezéstervezetnél
  32. Ugrás GYIK: Megvalósítás . Letöltve: 2013. november 11. Az eredetiből archiválva : 2019. január 7..
  33. https://gcc.gnu.org/gcc-4.6/changes.html Archiválva : 2013. december 2. a Wayback Machine -nél "A Go programozási nyelv támogatása hozzáadva a GCC-hez."
  34. A gccgo - The Go programozási nyelv beállítása és használata . golang.org. Letöltve: 2018. november 23. Az eredetiből archiválva : 2018. november 23.
  35. GCC 8 Release Series – Változások, új szolgáltatások és javítások – GNU Project – Free Software Foundation (FSF  ) . gcc.gnu.org. Letöltve: 2018. november 23. Az eredetiből archiválva : 2018. november 29.
  36. go-llvm Archiválva : 2014. szeptember 11. a Wayback Machine -nál ; áthelyezve ide: llvm-mirror/llgo Archiválva : 2018. június 11. a Wayback Machine -nál
  37. Archivált másolat . Letöltve: 2018. november 2. Az eredetiből archiválva : 2017. március 22.
  38. gollvm - Git a Google-nál . Letöltve: 2018. november 2. Az eredetiből archiválva : 2018. december 8..
  39. Gollvm: Google Working On LLVM-Based Go Compiler  , Phoronix (2017. május 29.) . Az eredetiből archiválva : 2018. október 12. Letöltve: 2018. november 2.
  40. interp - GoDoc . Letöltve: 2018. november 2. Az eredetiből archiválva : 2019. május 29.

Irodalom

  • Donovan, Alan A. A., Kernighan, Brian, W. A Go programozási nyelv = The Go programozási nyelv. - M . : LLC "I.D. Williams", 2016. - P. 432. - ISBN 978-5-8459-2051-5 .
  • Mészáros M., Farina M. Menj a gyakorlatban. - " DMK Press ", 2017. - P. 374. - ISBN 978-5-97060-477-9 .
  • Mark Summerfield. Menj programozni. Alkalmazások fejlesztése a XXI. - " DMK Press ", 2013. - P. 580. - ISBN 978-5-94074-854-0 .

Linkek