Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 4. september 2022; checks kræver 4 redigeringer .
Sprog klasse multithreaded , imperativ , struktureret , objektorienteret [1] [2]
Udførelsestype kompileret
Dukkede op i 10. november 2009
Forfatter Robert Grismer , Rob Pike og Ken Thompson
Udvikler Google , Rob Pike , Ken Thompson , The Go Authors [d] og Robert Grismer [d]
Filtypenavn _ .go
Frigøre
Type system streng , statisk , med typeslutning
Blev påvirket 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 og Occam
Licens BSD
Internet side go.dev
OS DragonFly BSD , FreeBSD , Linux , macOS , NetBSD , OpenBSD , Plan 9 , Solaris , Microsoft Windows , iOS , Android , AIX og Illumos
 Mediefiler på Wikimedia Commons

Go (ofte også golang ) er et kompileret programmeringssprog med flere tråde udviklet internt af Google [8] . Udviklingen af ​​Go begyndte i september 2007, med Robert Grismer , Rob Pike og Ken Thompson [9] , som tidligere arbejdede på Inferno -operativsystemudviklingsprojektet, direkte involveret i dets design . Sproget blev officielt introduceret i november 2009 . I øjeblikket er understøttelse af den officielle compiler udviklet af skaberne af sproget til operativsystemer FreeBSD , OpenBSD , Linux , macOS , Windows , DragonFly BSD , Plan 9 , Solaris , Android , AIX . [10] . Go understøttes også af gcc- kompilersættet , og der er flere uafhængige implementeringer. En anden version af sproget er under udvikling.

Titel

Navnet på det sprog, Google har valgt, er næsten det samme som navnet på programmeringssproget Go! , skabt af F. Gee. McCabe og C. L. Clark i 2003 [11] . Navnet diskuteres på Go-siden [11] .

På sprogets hjemmeside og generelt i internetpublikationer bruges ofte det alternative navn "golang".

Formål, ideologi

Go-sproget blev udviklet som et programmeringssprog til at skabe højeffektive programmer, der kører på moderne distribuerede systemer og multi-core processorer. Det kan ses som et forsøg på at skabe en erstatning for C og C++ sprogene under hensyntagen til de ændrede computerteknologier og den akkumulerede erfaring i udviklingen af ​​store systemer [12] . Med ordene fra Rob Pike [12] , "Go blev designet til at løse virkelige softwareudviklingsproblemer hos Google." Han opregner følgende som hovedproblemer:

De vigtigste krav til sproget var [13] :

Go blev skabt med en forventning om, at programmer på den ville blive oversat til objektkode og eksekveret direkte uden at kræve en virtuel maskine , så et af kriterierne for at vælge arkitektoniske løsninger var evnen til at sikre hurtig kompilering til effektiv objektkode og fraværet af overdreven krav til dynamisk support.

Resultatet var et sprog "som ikke var et gennembrud, men som alligevel var et fremragende værktøj til udvikling af store softwareprojekter" [12] .

Selvom en tolk er tilgængelig til Go , er der praktisk talt ikke noget stort behov for det, da kompileringshastigheden er hurtig nok til at tillade interaktiv udvikling.

Hovedtræk ved sproget

Hovedtræk ved Go-sproget [9] :

Go indeholder ikke mange af de populære syntaktiske funktioner, der er tilgængelige i andre moderne applikationsprogrammeringssprog. I mange tilfælde er dette forårsaget af en bevidst beslutning fra udviklerne. Korte begrundelser for de valgte designbeslutninger kan findes i "Ofte stillede spørgsmål" [9] om sproget, mere detaljeret - i de artikler og diskussioner, der er offentliggjort på sprogets websted, under hensyntagen til forskellige designmuligheder. I særdeleshed:

Syntaks

Syntaksen for Go-sproget ligner den for C -sproget , med elementer lånt fra Oberon og scriptsprog .

Alfabet

Go er et sprog, der skelner mellem store og små bogstaver, med fuld Unicode-understøttelse af strenge og identifikatorer.

En identifikator kan traditionelt være en hvilken som helst ikke-tom sekvens af bogstaver, tal og en understregning, der starter med et bogstav og ikke matcher nogen af ​​Go-nøgleordene. "Bokstaver" refererer til alle Unicode-tegn, der falder ind under kategorierne "Lu" (store bogstaver), "Ll" (små bogstaver), "Lt" (store bogstaver), "Lm" (modificerende bogstaver) eller "Lo" ( andre bogstaver), under "tal" - alle tegn fra kategorien "Nd" (tal, decimaltal). Der er således intet til hinder for at bruge kyrillisk i f.eks. identifikatorer.

Identifikatorer, der kun adskiller sig i store og små bogstaver, er forskellige. Sproget har en række konventioner for brugen af ​​store og små bogstaver. Især bruges kun små bogstaver i pakkenavne. Alle Go søgeord er skrevet med små bogstaver. Variabler, der starter med store bogstaver, kan eksporteres (offentlige), og de, der starter med små bogstaver, kan ikke eksporteres (private).

Strengliteraler kan bruge alle Unicode-tegn uden begrænsninger. Strenge er repræsenteret som sekvenser af UTF-8- tegn .

Pakker

Ethvert Go-program inkluderer en eller flere pakker. Pakken, som en kildekodefil hører til, er givet af pakkebeskrivelsen i begyndelsen af ​​filen. Pakkenavne har de samme begrænsninger som identifikatorer, men kan kun indeholde små bogstaver. Goroutine-pakkesystemet har en træstruktur, der ligner et bibliotekstræ. Alle globale objekter (variabler, typer, grænseflader, funktioner, metoder, elementer af strukturer og grænseflader) er tilgængelige uden begrænsninger i den pakke, hvori de er erklæret. Globale objekter, hvis navne begynder med et stort bogstav, kan eksporteres.

For at bruge objekter eksporteret af en anden pakke i en Go-kodefil, skal pakken importeres ved hjælp af import.

pakke hoved /* Import */ import ( "fmt" // Standardpakke til formateret output "database/sql" // Importer indlejret pakke w "os" // Importer med alias . "math" // Importer uden kvalifikation ved brug af _ "gopkg.in/goracle.v2" // Pakken har ingen eksplicitte referencer i koden ) func main () { for _ , arg := range w . Args { // Adgang til Args-arrayet, der er erklæret i "os"-pakken via fmt -aliaset . Println ( arg ) // Kaldning af Println()-funktionen erklæret i pakken "fmt" med pakkenavn } var db * sql . db = sql . Open ( driver , dataSource ) // Navne fra den indlejrede pakke er kvalificeret // kun efter navnet på selve pakken (sql) x := Sin ( 1.0 ) // call math.Sin() - kvalifikation ved pakkenavnet math er ikke nødvendig // fordi den er importeret uden et navn // Der er ingen reference til "goracle.v2"-pakken i koden, men den vil blive importeret. }

Den viser stierne til importerede pakker fra src-biblioteket i kildetræet, hvis position er givet af miljøvariablen GOPATH, mens det for standardpakker blot angives navnet. En streng, der identificerer en pakke, kan indledes med et alias, i hvilket tilfælde den vil blive brugt i kode i stedet for pakkenavnet. Importerede objekter er tilgængelige i den fil, der importerer dem med en fuld kvalifikation som " пакет.Объект". Hvis en pakke importeres med en prik i stedet for et alias, vil alle de navne, den eksporterer, være tilgængelige uden forbehold. Denne funktion bruges af nogle systemværktøjer, men dens brug af programmøren anbefales ikke, da eksplicit kvalifikation giver beskyttelse mod navnekollisioner og "umærkelige" ændringer i kodeadfærd. Det er ikke muligt uden kvalifikation at importere to pakker, der eksporterer det samme navn.

Importen af ​​pakker i Go er stramt kontrolleret: Hvis en pakke importeres af et modul, skal der bruges mindst ét ​​navn, der eksporteres af den pågældende pakke, i koden for det pågældende modul. Go-kompileren behandler import af en ubrugt pakke som en fejl; en sådan løsning tvinger udvikleren til konstant at holde importlisterne ajour. Dette skaber ingen vanskeligheder, da Go programmeringsstøtteværktøjer (editorer, IDE'er) normalt giver automatisk kontrol og opdatering af importlister.

Når en pakke indeholder kode, der kun bruges gennem introspektion , er der et problem: import af en sådan pakke er nødvendig for at inkludere den i programmet, men vil ikke blive tilladt af compileren, da den ikke er direkte tilgået. _Anonym import er tilvejebragt for sådanne tilfælde: " " (enkelt understregning) er angivet som et alias ; en pakke importeret på denne måde vil blive kompileret og inkluderet i programmet, hvis den ikke er eksplicit refereret i koden. En sådan pakke kan dog ikke bruges eksplicit; dette forhindrer importkontrol i at blive omgået ved at importere alle pakker som anonyme.

Et eksekverbart Go-program skal indeholde en pakke med navnet main, som skal indeholde en funktion main()uden parametre og en returværdi. Funktionen main.main()er "programmets krop" - dens kode køres, når programmet starter. Enhver pakke kan indeholde en funktion init() - den vil blive kørt, når programmet er indlæst, før det begynder at udføre, før en funktion kaldes i denne pakke og i enhver pakke, der importerer denne. Hovedpakken initialiseres altid sidst, og alle initialiseringer udføres før funktionen begynder at udføre main.main().

Moduler

Go-pakkesystemet blev designet med den antagelse, at hele udviklingsøkosystemet eksisterer som et enkelt filtræ, der indeholder opdaterede versioner af alle pakker, og når nye versioner dukker op, bliver det fuldstændigt omkompileret. For applikationsprogrammering ved hjælp af tredjepartsbiblioteker er dette en ret stærk begrænsning. I virkeligheden er der ofte begrænsninger på de versioner af pakker, der bruges af en eller anden kode, såvel som situationer, hvor forskellige versioner (grene) af et projekt bruger forskellige versioner af bibliotekspakker.

Siden version 1.11 har Go understøttet såkaldte moduler . Et modul er en specielt beskrevet pakke, der indeholder information om dens version. Når et modul importeres, er den anvendte version fast. Dette giver byggesystemet mulighed for at kontrollere, om alle afhængigheder er opfyldt, automatisk opdatere importerede moduler, når forfatteren foretager kompatible ændringer til dem, og blokere opdateringer til ikke-bagudkompatible versioner. Moduler formodes at være en løsning (eller en meget lettere løsning) på problemet med afhængighedsstyring.

Kommentarer og semikolon

Go bruger begge typer kommentarer i C-stil: indlejrede kommentarer (begynder med // ...) og blokkommentarer (/* ... */). En linjekommentar behandles af compileren som en ny linje. Blok, placeret på en linje - som et mellemrum, på flere linjer - som en ny linje.

Semikolonet i Go bruges som en obligatorisk separator i nogle operationer (hvis, for, switch). Formelt set bør det også afslutte hver kommando, men i praksis er det ikke nødvendigt at sætte et sådant semikolon i slutningen af ​​linjen, da compileren selv tilføjer semikolon til slutningen af ​​hver linje, undtagen tomme tegn, i slutningen af ​​linjen. identifikator, tal, en bogstavelig karakter, en streng, bruddet, fortsæt, gennemfald, returner nøgleord, en inkrementer eller dekrementeringskommando (++ eller --) eller en afsluttende parentes, firkant eller krøllet klammeparentes (en vigtig undtagelse er, at et komma er ikke inkluderet i ovenstående liste). Heraf følger to ting:

  • I praksis er et semikolon kun nødvendigt i nogle formater af if, for, switch-sætninger og for at adskille kommandoer placeret på samme linje. Derfor er der meget få semikoloner i Go-koden.
  • En bivirkning af den automatiske semkolonisering af compileren er, at du ikke nogen steder i programmet, hvor et mellemrum er tilladt, kan bruge et linjeskift. Især i beskrivelser, initialiseringskommandoer, og hvis, for switch-konstruktioner, kan du ikke flytte åbningsbøjlen til følgende linje:
func g () // ! { // FORKERT } hvis x { } //! andet { // FORKERT } func g (){ // RIGHT } hvis x { } andet { // TRUE } Her vil compileren i de to første tilfælde indsætte et semikolon i linjen markeret med en kommentar med et udråbstegn, da linjen slutter (henholdsvis ignorerer mellemrum og kommentarer) med runde og krøllede afsluttende parenteser. Som et resultat vil syntaksen af ​​funktionserklæringen i det første tilfælde og den betingede operator i det andet tilfælde blive brudt. På samme måde kan du ikke flytte kommaet til næste linje i en liste over elementer adskilt af kommaer: func f ( i //!, k int // !, s // !, t streng ) streng { // FORKERT } func f ( i , k int , s , t streng ) streng { // TRUE } Når du flytter et komma til næste linje, slutter den aktuelle linje med en identifikator, og et semikolon sættes automatisk i slutningen af ​​den, hvilket overtræder listesyntaksen (et komma, som nævnt ovenfor, er en undtagelse fra reglen; compileren tilføjer ikke et semikolon efter det). Sproget dikterer således en bestemt stil at skrive kode på. Go-kompileren kommer med værktøjet gofmt, som giver korrekt og ensartet formatering af kildetekster. Al tekst i Go-standardbiblioteket er formateret af dette værktøj.

Indbyggede datatyper

Sproget indeholder et ret standardsæt af simple indbyggede datatyper: heltal, flydende kommatal, tegn, strenge, booleaner og et par specielle typer.

Heltal

Der er 11 heltalstyper:

  • Signerede heltal med fast størrelse - int8, int16, int32, int64. Disse er signerede heltal repræsenteret i to's komplement , størrelsen af ​​værdier af disse typer er henholdsvis 8, 16, 32, 64 bit. Værdiområdet er fra −2 n−1 til 2 n−1 −1, hvor n er størrelsen på typen.
  • Heltal uden fortegn i fast størrelse - uint8, uint16, uint32, uint64. Tallet i typenavnet, som i det foregående tilfælde, angiver størrelsen, men værdiområdet er fra 0 til 2 n -1.
  • intog uint er henholdsvis signerede og usignerede heltal. Størrelsen på disse typer er den samme og kan være 32 eller 64 bit, men er ikke fastsat af sprogspecifikationen og kan vælges af implementeringen. Det antages, at den mest effektive størrelse på målplatformen vil blive valgt til dem.
  • byte - synonym uint8. Det er som regel beregnet til at arbejde med uformaterede binære data.
  • rune er et synonym uint32for , repræsenterer et Unicode-tegn.
  • uintptr er en heltalsværdi uden fortegn, hvis størrelse er implementeringsdefineret, men skal være stor nok til at gemme den fulde pointerværdi for målplatformen i en variabel af denne type.

Skaberne af sproget anbefaler kun at bruge standardtypen til at arbejde med tal inde i programmet int. Typer med faste størrelser er designet til at arbejde med data modtaget fra eller videregivet til eksterne kilder, når det er vigtigt for kodens rigtighed at angive en bestemt størrelse af typen. Typerne er synonymer byteog runeer designet til at arbejde med henholdsvis binære data og symboler. Typen uintptrer kun nødvendig for interaktion med ekstern kode, for eksempel i C.

Flydende kommatal

Flydende kommatal er repræsenteret af to typer, float32og float64. Deres størrelse er henholdsvis 32 og 64 bit, implementeringen overholder IEEE 754- standarden . Rækken af ​​værdier kan fås fra standardpakken math.

Numeriske typer med ubegrænset præcision

Go-standardbiblioteket indeholder også pakken big, som giver tre typer med ubegrænset præcision: big.Int, big.Ratog big.Float, som repræsenterer henholdsvis heltal, rationaler og flydende kommatal; størrelsen af ​​disse tal kan være hvad som helst og er kun begrænset af mængden af ​​tilgængelig hukommelse. Da operatører i Go ikke er overbelastet, implementeres beregningsoperationer på tal med ubegrænset præcision som almindelige metoder. Ydeevnen af ​​beregninger med store tal er naturligvis væsentligt ringere end de indbyggede numeriske typer, men når man løser visse typer beregningsproblemer, kan brug af en pakke bigvære at foretrække frem for manuel optimering af en matematisk algoritme.

Komplekse tal

Sproget giver også to indbyggede typer for komplekse tal, complex64og complex128. Hver værdi af disse typer indeholder et par reelle og imaginære dele med henholdsvis typer float32og float64. Du kan oprette en værdi af en kompleks type i kode på en af ​​to måder: enten ved at bruge en indbygget funktion complex()eller ved at bruge en imaginær bogstavelig i et udtryk. Du kan få de reelle og imaginære dele af et komplekst tal ved at bruge funktionerne real()og imag().

var x kompleks128 = kompleks ( 1 , 2 ) // 1 + 2i y := 3 + 4i // 3 + 4i, hvor 4 er et tal efterfulgt af et i-suffiks // er en imaginær fmt- literal . Println ( x * y ) // udskriver "(-5+10i)" fmt . Println ( real ( x * y )) // udskriver "-5" fmt . Println ( billede ( x * y )) // vil udskrive "10" Booleske værdier

Den boolske type booler ret almindelig - den inkluderer de foruddefinerede værdier trueog falseangiver henholdsvis sand og falsk. I modsætning til C er booleaner i Go ikke numeriske og kan ikke direkte konverteres til tal.

Strings

Strengtypeværdier stringer uforanderlige byte-arrays, der indeholder UTF-8. Dette forårsager en række specifikke træk ved strenge (for eksempel i det generelle tilfælde er længden af ​​en streng ikke lig med længden af ​​den matrix, der repræsenterer den, dvs. antallet af tegn indeholdt i den er ikke lig med antallet af bytes i det tilsvarende array). For de fleste applikationer, der behandler hele strenge, er denne specificitet ikke vigtig, men i de tilfælde, hvor programmet direkte skal behandle specifikke runer (Unicode-tegn), unicode/utf8kræves en pakke, der indeholder hjælpeværktøjer til at arbejde med Unicode-strenge.

Typedeklarationer

For alle datatyper, inklusive indbyggede, kan nye analoge typer erklæres, som gentager alle originalernes egenskaber, men som er inkompatible med dem. Disse nye typer kan også valgfrit deklarere metoder. Brugerdefinerede datatyper i Go er pointere (erklæret med symbolet *), arrays (erklæret med firkantede parenteser), strukturer ( struct), funktioner ( func), grænseflader ( interface), mappings ( map) og kanaler ( chan). Deklarationerne af disse typer specificerer typerne og muligvis identifikatorerne for deres elementer. Nye typer erklæres med søgeordet type:

type PostString string // Skriv "string", svarende til indbygget type StringArray [] string // Array type med string type elementer type Person struct { // Struct type name string // felt af standard strengtype post PostString // felt af den tidligere erklærede brugerdefinerede strengtype bdate time . Tid // felt af typen Tid, importeret fra pakken tid edate time . Tidschef * Person // pointer field infer [ ]( * Person ) // array field } type InOutString chan string // kanaltype til at sende strenge type CompareFunc func ( a , b interface {}) int // funktionstype.

Siden version Go 1.9 er deklaration af typealiaser (aliaser) også tilgængelig:

type TitleString = streng // "TitleString" er et alias for den indbyggede type strengtype Integer = int64 // "Integer" er et alias for den indbyggede 64-bit heltalstype

Et alias kan erklæres for enten en systemtype eller enhver brugerdefineret type. Den grundlæggende forskel mellem aliaser og almindelige typedeklarationer er, at deklarationen skaber en ny type, som ikke er kompatibel med originalen, selvom der ikke tilføjes ændringer til den originale type i deklarationen. Et alias er blot et andet navn for den samme type, hvilket betyder, at aliaset og den originale type er fuldstændigt udskiftelige.

Strukturfelter kan have tags i beskrivelsen - vilkårlige sekvenser af tegn omgivet af bagerste anførselstegn:

// Struktur med feltmærker type XMLInvoices struct { XMLName xml . Navn `xml:"INVOICES"` Version int `xml:"version,attr"` Faktura [] * XMLInvoice `xml:"INVOICE"` }

Tags ignoreres af compileren, men information om dem er placeret i koden og kan læses ved hjælp af funktionerne i pakken reflectinkluderet i standardbiblioteket. Typisk bruges tags til at levere typemarshaling til lagring og gendannelse af data på eksterne medier eller interaktion med eksterne systemer, der modtager eller transmitterer data i deres egne formater. Eksemplet ovenfor bruger tags behandlet af standardbiblioteket til at læse og skrive data i XML-format.

Variable erklæringer

Syntaksen til at deklarere variabler er hovedsageligt løst i Pascals ånd: Deklarationen begynder med nøgleordet var, efterfulgt af variabelnavnet gennem separatoren, derefter, gennem separatoren, dens type.

C++
var v1 int const v2 string var v3 [ 10 ] int var v4 [ ] int var v5 struct { f int } var v6 * int /* pointer aritmetic ikke understøttet */ var v7 map [ string ] int var v8 func ( a int ) int int v1 ; const std :: stringv2 ; _ /* om */ intv3 [ 10 ] ; int * v4 ; /* om */ struct { int f ; } v5 ; int * v6 ; std :: uordnet_kort v7 ; /* om */ int ( * v8 )( int a );

Variabel erklæring kan kombineres med initialisering:

var v1 int = 100 var v2 string = "Hej!" 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 = { "one" : 1 , "to" : 2 , "three" : 3 } var v8 func ( a int ) int = func ( a int ) int { returner a + 1 }

Hvis en variabel ikke udtrykkeligt initialiseres , når den erklæres , initialiseres den automatisk til "nullværdi" for den givne type. Nullværdien for alle numeriske typer er 0, for en type string er det den tomme streng, for pointere  er den nil. Strukturer initialiseres som standard med sæt nulværdier for hvert af felterne inkluderet i dem, arrayelementer initialiseres med nulværdier af den type, der er angivet i arraydefinitionen.

Annoncer kan grupperes:

var ( i int m float )

Automatisk typeslutning

Go-sproget understøtter også automatisk typeslutning . Hvis en variabel initialiseres, når den er erklæret, kan dens type udelades - typen af ​​det udtryk, der er tildelt den, bliver variablens type. For bogstaver (tal, tegn, strenge) definerer sprogstandarden specifikke indbyggede typer, som hver sådan værdi tilhører. For at initialisere en variabel af en anden type, skal der anvendes en eksplicit typekonvertering på det bogstavelige.

var p1 = 20 // p1 int - det heltal literal 20 er af typen int. var p2 = uint ( 20 ) // p2 uint - værdi eksplicit castet til uint. var v1 = & p1 // v1 *int er en pointer til p1, for hvilken int-typen udledes. var v2 = & p2 // v2 *uint er en pointer til p2, som eksplicit initialiseres som et heltal uden fortegn.

For lokale variabler er der en forkortet form for erklæring kombineret med initialisering ved hjælp af typeinferens:

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

Opgaver

Go bruger symbolet som en opgaveoperator =:

a = b // Sæt variabel a til b

Som nævnt ovenfor er der en form for definition af en variabel med automatisk typeinferens kombineret med initialisering, der udadtil ligner tildeling i Pascal :

v1 := v2 // ligner var v1 = v2

Go-kompileren holder nøje styr på definitioner og opgaver og adskiller den ene fra den anden. Da omdefinering af en variabel med samme navn er forbudt i ét omfang, inden for en kodeblok, kan en variabel kun vises til venstre for tegnet :=én gang:

a := 10 // Erklæring og initialisering af en heltalsvariabel a. b := 20 // Erklæring og initialisering af en heltalsvariabel b. ... a := b // FEJL! Forsøg på at omdefinere en.

Go gør det muligt at udføre flere opgaver parallelt:

i , j = j , i // Byt i- og j-værdier.

I dette tilfælde skal antallet af variable til venstre for tildelingstegnet nøjagtigt svare til antallet af udtryk til højre for tildelingstegnet.

Parallel tildeling er også mulig ved brug af :=. Dets ejendommelighed er, at der blandt variablerne til venstre for tegnet :=kan være allerede eksisterende. I dette tilfælde vil nye variable blive oprettet, eksisterende vil blive genbrugt. Denne syntaks bruges ofte til fejlhåndtering:

x , err := SomeFunction () // Funktionen returnerer to værdier (se nedenfor), // to variable er erklæret og initialiseret. if ( fejl != nul ) { returner nul } y , err := SomeOtherFunction () // Kun y erklæres her, err er blot tildelt en værdi.

I den sidste linje i eksemplet tildeles den første værdi, der returneres af funktionen, til den nye variabel y, den anden til den allerede eksisterende variabel err, som bruges gennem hele koden til at placere den sidste fejl returneret af de kaldte funktioner. Hvis ikke for denne funktion af operatoren :=, ville man i det andet tilfælde skulle erklære en ny variabel (for eksempel err2) eller separat erklære yog derefter bruge den sædvanlige parallelle tildeling.

Go implementerer "kopi-på-tildeling" semantik, hvilket betyder, at en opgave resulterer i at lave en kopi af værdien af ​​den oprindelige variabel og placere denne kopi i en anden variabel, hvorefter værdierne af variablerne er forskellige og ændre en af dem ændrer ikke den anden. Dette gælder dog kun for indbyggede skalartyper, strukturer og arrays med en given længde (det vil sige typer, hvis værdier er allokeret på stakken). Arrays af ubestemt længde og mappings er allokeret på heapen , variabler af disse typer indeholder faktisk referencer til objekter, når de tildeles, kopieres kun referencen, men ikke selve objektet. Nogle gange kan dette føre til uventede virkninger. Overvej to næsten identiske eksempler:

type vektor [ 2 ] float64 // Længden af ​​arrayet indstilles eksplicit v1 := vektor { 10 , 15.5 } // Initialisering - v1 indeholder selve arrayet v2 := v1 // Array v1 kopieres til array v2 v2 [ 0 ] = 25.3 // Ændrede kun v2 fmt -arrayet . Println ( v1 ) // Udskriver "[10 15.5]" - det originale array er ikke ændret. fmt . Println ( v2 ) // Udskriver "[25.3 15.5]"

Her er typen vectordefineret som en matrix af to tal. Tildeling af sådanne arrays opfører sig på samme måde som tildeling af tal og strukturer.

Og i det følgende eksempel adskiller koden sig med præcis ét tegn: typen vectorer defineret som et array med en ubestemt størrelse. Men denne kode opfører sig helt anderledes:

type vektor [] float64 // Array med udefineret længde v1 := vektor { 10 , 15.5 } // Initialisering - v1 indeholder array reference v2 := v1 // Array reference er kopieret fra v1 til v2 v2 [ 0 ] = 25.3 / / Kan kun tænkes at ændre v2 fmt -arrayet . Println ( v1 ) // Udskriver "[25.3 15.5]" - det originale array er ÆNDRET! fmt . Println ( v2 ) // Udskriver "[25.3 15.5]"

På samme måde som i det andet eksempel opfører kortlægninger og grænseflader sig. Desuden, hvis strukturen har et felt af en reference- eller grænsefladetype, eller et felt er en dimensionsløs matrix eller mapping, så vil kun referencen også blive kopieret, når der tildeles en sådan struktur, det vil sige, at felterne i forskellige strukturer begynder at pege på de samme objekter i hukommelsen.

For at undgå denne effekt skal du eksplicit bruge en systemfunktion, copy()der garanterer oprettelsen af ​​en anden instans af objektet.

Argumenter til funktioner og metoder

erklæres således:

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

Funktioner kan returnere flere værdier

Typerne af sådanne værdier er omgivet af parentes:

func f ( a , b int ) ( int , streng ) { return a + b , "addition" }

Funktionsresultater kan også navngives:

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

Navngivne resultater anses for at være erklæret umiddelbart efter funktionsoverskriften med nul begyndelsesværdier. Retursætningen i en sådan funktion kan bruges uden parametre, i hvilket tilfælde resultaterne, efter at være vendt tilbage fra funktionen, vil have de værdier, der blev tildelt dem under udførelsen. Så i eksemplet ovenfor vil funktionen returnere et par heltalsværdier, en større end dens parametre.

Flere værdier, der returneres af funktioner, tildeles variabler ved at angive dem adskilt med kommaer, mens antallet af variabler, som resultatet af funktionskaldet er tildelt, skal svare nøjagtigt til antallet af værdier, der returneres af funktionen:

første , anden := incTwo ( 1 , 2 ) // first = 2, second = 3 first := incTwo ( 1 , 2 ) // FORKERT - ingen variabel tildelt det andet resultat

Pseudovariabel "_"

I modsætning til Pascal og C, hvor deklarering af en lokal variabel uden at bruge den senere, eller tab af værdien af ​​en lokal variabel (når værdien tildelt til variablen så ikke læses nogen steder) kun kan forårsage en compiler-advarsel, betragtes denne situation i Go en sprogfejl og fører til, at det er umuligt at kompilere programmet. Dette betyder især, at programmøren ikke kan ignorere værdien (eller en af ​​værdierne), der returneres af funktionen, blot ved at tildele den til en variabel og nægte at bruge den yderligere. Hvis det bliver nødvendigt at ignorere en af ​​de værdier, der returneres af et funktionskald, bruges en foruddefineret pseudovariabel med navnet "_" (én understregning). Det kan angives hvor som helst, hvor der skal være en variabel, der tager en værdi. Den tilsvarende værdi vil ikke blive tildelt nogen variabel og vil simpelthen gå tabt. Betydningen af ​​en sådan arkitektonisk beslutning er at identificere et muligt tab af beregningsresultater på kompileringsstadiet: en utilsigtet udeladelse af værdibehandling vil blive opdaget af compileren, og brugen af ​​"_" pseudovariablen vil indikere, at programmøren bevidst ignoreret resultaterne. I det følgende eksempel, hvis kun én af de to værdier returneret af incTwo-funktionen er nødvendig, skal "_" angives i stedet for den anden variabel:

første := incTwo ( 1 , 2 ) // UGYLDIG først , _ := incTwo ( 1 , 2 ) // TRUE, andet resultat ikke brugt

Variablen "_" kan angives i opgavelisten et vilkårligt antal gange. Alle funktionsresultater, der matcher "_" vil blive ignoreret.

Udskudt opkaldsmekanisme udskyd

Det udskudte opkald erstatter flere syntaktiske funktioner på én gang, især undtagelsesbehandlere og garanterede færdiggørelsesblokke. Et funktionskald forud for nøgleordet defer parametriseres på det punkt i programmet, hvor det er placeret, og udføres umiddelbart før programmet forlader omfanget, hvor det blev erklæret, uanset hvordan og af hvilken grund denne exit sker. Hvis en enkelt funktion indeholder flere defer-deklarationer, udføres de tilsvarende kald sekventielt efter funktionen slutter, i omvendt rækkefølge. Nedenfor er et eksempel på brug af defer som en garanteret færdiggørelsesblok [15] :

// Funktion, der kopierer filen func CopyFile ( dstName , srcName string ) ( skrevet int64 , err error ) { src , err := os . Åbn ( srcName ) // Åbn kildefilen hvis err != nul { // Check return // Hvis det mislykkes, returner med en fejl } // Hvis du kom hertil, blev kildefilen åbnet defer src . Luk () // Forsinket opkald: src.Close() vil blive kaldt, når CopyFile er fuldført dst , fejl := os . Opret ( dstName ) // Åbn destinationsfilen hvis fejl != nul { // Tjek og returner ved fejlretur } udskyd dst . Luk () // Forsinket opkald: dst.Close() kaldes når CopyFile er færdig returnio . _ Kopier ( dst , src ) // Kopier data og returner fra funktion // Efter alle operationer vil blive kaldt: først dst.Close(), derefter src.Close() }

Løkke- og forgreningsforhold

I modsætning til de fleste sprog med C-lignende syntaks, har Go ikke parenteser til betingede konstruktioner for, if, switch:

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

Cykler

Go bruger en loop-konstruktion til at organisere alle slags loops  for.

for i < 10 { // loop med forudsætning, svarende til mens i C } for i := 0 ; i < 10 ; i ++ { // sløjfe med en tæller, svarende til for i C } for { // uendelig løkke // Afslutning af løkken skal håndteres manuelt, // gøres normalt med retur eller pause } for { // loop med postcondition ... // loop body if i >= 10 { // exit condition break } } for i , v := range arr { // loop gennem samlingen (array, slice, display) arr // i - index (eller key) for det aktuelle element // v - kopi af værdien af ​​det aktuelle array-element } for i := range arr { // går gennem samlingen, er det kun indekset, der bruges } for _ , v := range arr { // loop through collection, brug kun elementværdier ​} for range arr { // Loop gennem samlingen uden variabler (samlingen bruges // kun som en iterationstæller). } for v := range c { // sløjfe gennem kanalen: // v vil læse værdier fra kanal c, // indtil kanalen lukkes af en samtidig // goroutine }

Multiple choice-operator

Syntaksen for multiple choice-operatøren switchhar en række funktioner. Først og fremmest, i modsætning til C, er brugen af ​​operatøren ikke påkrævet break: efter at den valgte gren er blevet behandlet, afsluttes udførelsen af ​​operatøren. Hvis du tværtimod ønsker, at den næste gren skal fortsætte behandlingen efter den valgte gren, skal du bruge operatoren fallthrough:

switch værdi { case 1 : fmt . Println ( "One" ) fallthrough // Dernæst vil "case 0:"-grenen blive udført case 0 : fmt . println ( "Nul" ) }

Her, når value==1to linjer vil blive vist, "En" og "Nul".

Valgudtrykket og dermed alternativerne i switch-sætningen kan være af enhver type, det er muligt at opregne flere muligheder i en gren:

skifte tegn [ kode ]. kategori { case "Lu" , "Ll" , "Lt" , "Lm" , "Lo" : ... case "Nd" : ... default : ... }

Fraværet af et valgudtryk er tilladt, i hvilket tilfælde logiske betingelser skal skrives i alternativerne. Den første gren udføres, hvis betingelse er sand:

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

En vigtig detalje: Hvis en af ​​grenene med betingelsen slutter med operatøren fallthrough, vil den næste efter denne gren blive behandlet, uanset om dens betingelse er opfyldt . Hvis du ønsker, at den næste gren kun skal behandles, hvis dens betingelse er opfyldt, skal du bruge sekventielle konstruktioner if.

Arkitektoniske træk

Håndtering af fejl og undtagelser

Go-sproget understøtter ikke den strukturerede undtagelseshåndteringssyntaks, der er typisk for de fleste moderne sprog , som involverer at smide undtagelser med en speciel kommando (normalt throweller raise) og håndtere dem i en blok try-catch. I stedet anbefales det at bruge fejlreturn som et af funktionens resultater (hvilket er praktisk, da en funktion i Go kan returnere mere end én værdi):

  • I den sidste parameter returnerer funktionen et fejlobjekt eller en nul-pointer nil, hvis funktionen blev udført uden fejl. Typen af ​​fejl er normalt en biblioteksgrænseflade error.
  • Objektet, der returneres af funktionen, kontrolleres, og fejlen, hvis nogen, håndteres. Hvis en fejl på opkaldsstedet ikke kan håndteres tilstrækkeligt, returneres den normalt som et resultat af den aktuelle funktion, eller der oprettes en ny fejl baseret på den og returneres.
func ReadFile ( srcName string )( resultatstreng , err error ) { file , err : = os . Åbn ( srcName ) if err != nil { // Generer en ny fejl med kvalificerende tekst return nil , fmt . Errorf ( "læse fil %q: %w" , srcName , err ) } ... // Yderligere udførelse af funktionen hvis der ikke var nogen fejl returner resultatet , nul // Returner resultat og tom fejl hvis det lykkedes }
  • Det er umuligt at ignorere fejlen, der returneres fra funktionen (i eksemplet ovenfor, kontroller ikke værdien af ​​variablen err), fordi initialisering af en variabel uden yderligere brug i Go-sproget fører til en kompileringsfejl. Denne begrænsning kan omgås ved at erstatte fejl-pseudovariablen _, men dette er tydeligt, når man ser på koden.

Mange kritikere af sproget mener, at denne ideologi er værre end undtagelseshåndtering, da adskillige kontroller roder koden og ikke tillader al fejlhåndtering at blive koncentreret i blokke catch. Sprogets skabere betragter ikke dette som et alvorligt problem. En række fejlhåndteringsmønstre i Go er beskrevet (se f.eks. Rob Pikes artikel på den officielle Go-blog , russisk oversættelse ), som kan reducere mængden af ​​fejlhåndteringskode.

Når der opstår fatale fejl, der umuliggør yderligere programudførelse (f.eks. division med nul eller adgang til array-grænser), opstår der en paniktilstand , som som standard fører til, at programmet går ned med en fejlmeddelelse og en opkaldsstaksporing. Panik kan fanges og håndteres ved hjælp af den udskudte udførelseskonstruktion deferbeskrevet ovenfor. Funktionskaldet, der er specificeret i defer, foretages før det aktuelle omfang forlades, også i tilfælde af panik. Inde i funktionen kaldet i deferkan du kalde en standardfunktion recover() - den stopper systembehandlingen af ​​en panik og returnerer dens årsag i form af et objekt error, der kan behandles som en normal fejl. Men programmøren kan også genoptage en tidligere fanget panik ved at kalde standarden panic(err error).

// Programmet udfører en heltalsdeling // af dens første parameter med den anden // og udskriver resultatet. func main () { defer func () { err := recover () if v , ok := err .( error ); ok { // Håndtering af panik svarende til grænsefladefejl fmt . Fprintf ( os . Stderr , "Error %v \"%s\"\n" , err , v . Error ()) } else if err != nil { panic ( err ) // Håndtering af uventede fejl - re-raise the panik. } }() a , fejl := strconv . ParseInt ( os . Args [ 1 ], 10 , 64 ) hvis err != nil { panic ( 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 ) }

I eksemplet ovenfor kan der opstå fejl, når programargumenterne konverteres til heltal af funktionen strconv.ParseInt(). Det er også muligt at gå i panik, når man får adgang til os.Args-arrayet med et utilstrækkeligt antal argumenter, eller når man dividerer med nul, hvis den anden parameter er nul. For enhver fejlsituation genereres en panik, som behandles i opkaldet defer:

> dividere 10 5 10/5 = 2 > dividere 10 0 Error runtime.errorString "runtime error: heltal divider med nul" > dividere 10,5 2 Fejl *strconv.NumError "strconv.ParseInt: parsing "10.5": ugyldig syntaks" > divider 10 Error runtime.errorString "runtime error: index out of range"

En panik kan ikke udløses i en parallel-eksekverende goroutine (se nedenfor), men håndteres i en anden. Det anbefales heller ikke at "passere" panik over en pakkegrænse.

Multithreading

Go's threading-model blev arvet fra Active Oberon -sproget baseret på Tony Hoares CSP ved hjælp af ideer fra Occam- og Limbo -sprogene [9] , men funktioner som pi-calculus og kanalisering er også til stede.

Go giver dig mulighed for at oprette en ny tråd for programafvikling ved hjælp af nøgleordet go , som kører en anonym eller navngivet funktion i en nyoprettet goroutine (Go's term for coroutines ). Alle goroutiner inden for samme proces bruger et fælles adresseområde, der udføres på OS-tråde , men uden hård binding til sidstnævnte, hvilket gør det muligt for en kørende goroutine at forlade en tråd med en blokeret goroutine (venter f.eks. på at sende eller modtage en besked fra en kanal) og fortsæt videre. Runtime-biblioteket inkluderer en multiplexer til at dele det tilgængelige antal systemkerner blandt goroutiner. Det er muligt at begrænse det maksimale antal fysiske processorkerner, som programmet vil blive udført på. Go-runtime-bibliotekets selvunderstøttelse af goroutiner gør det nemt at bruge et stort antal goroutiner i programmer, hvilket langt overstiger grænsen for antallet af tråde, der understøttes af systemet.

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

Lukninger kan bruges i et go udtryk .

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

Til kommunikation mellem goroutiner bruges kanaler (den indbyggede type chan ), hvorigennem enhver værdi kan sendes. En kanal oprettes af den indbyggede funktion make(), som overføres kanalens type og (valgfrit) volumen. Som standard er kanallydstyrken nul. Sådanne kanaler er ubuffrede . Du kan indstille en hvilken som helst positiv heltalsvolumen for kanalen, så vil der blive oprettet en bufferkanal .

Et ubufret rør synkroniserer tæt læser- og forfattertråde, der bruger det. Når en forfattertråd skriver noget til et rør, holder den pause og venter, indtil værdien er blevet læst. Når en læsertråd forsøger at læse noget fra et rør, der allerede er skrevet til, læser den værdien, og begge tråde kan fortsætte med at udføre. Hvis der endnu ikke er skrevet nogen værdi til kanalen, holder læsetråden pause og venter på, at nogen skriver til kanalen. Det vil sige, ubuffrede rør i Go opfører sig på samme måde som rør i Occam eller rendezvous - mekanismen i Ada-sproget .

En bufferkanal har en værdibuffer, hvis størrelse er lig med kanalens størrelse. Når man skriver til et sådant rør, placeres værdien i rørets buffer, og writer-tråden fortsætter uden pause, medmindre rørets buffer er fuld på skrivetidspunktet. Hvis bufferen er fuld, suspenderes forfattertråden, indtil mindst én værdi er blevet læst fra kanalen. Læsertråden læser også en værdi fra et bufferet rør uden at holde pause, hvis der er ulæste værdier i rørets buffer; hvis kanalbufferen er tom, holder tråden pause og venter, indtil en anden tråd skriver en værdi til den.

Når du er færdig, kan kanalen lukkes med den indbyggede funktion close(). Et forsøg på at skrive til en privat kanal resulterer i panik, læsning fra en privat kanal sker altid uden pause og læser standardværdien. Hvis kanalen er bufferet og på tidspunktet for lukning indeholder den N tidligere skrevne værdier i bufferen, så udføres de første N læseoperationer, som om kanalen stadig var åben og læser værdierne fra bufferen, og først derefter vil læsningen fra kanalen returnere standardværdierne.

Operationen bruges til at sende en værdi til og fra en kanal <-. Når du skriver til en kanal, bruges den som en binær operator, når du læser - som en unær operator:

in := make ( chan string , 0 ) // Opret en ubufferet kanal ind ud := make ( chan int , 10 ) // Opret en bufferet kanal ud ... in <- arg // Skriv en værdi til kanalen i ... r1 := <- ud // læser fra kanalen ud ... r2 , ok := <- ud // læser med at tjekke om kanalen er lukket hvis ok { // hvis ok == sand - kanalen er åben ... } else { // hvis kanalen er lukket, så gør noget andet ... }

Operationen med at læse fra en kanal har to muligheder: uden kontrol og med kontrol for lukning af kanalen. Den første mulighed (læser r1 i eksemplet ovenfor) læser simpelthen den næste værdi ind i variablen; hvis kanalen er lukket, vil standardværdien blive indlæst i r1. Den anden mulighed (læser r2) læser, ud over værdien, en boolsk værdi - ok kanalstatusflaget, som vil være sandt, hvis dataene placeret der af en strøm er blevet læst fra kanalen, og falsk, hvis kanalen er lukket, og dens buffer er tom. Med denne operation kan læsetråden bestemme, hvornår inputkanalen er lukket.

Aflæsning fra et rør understøttes også ved hjælp af for-range loop-konstruktionen:

// Funktionen starter parallellæsning fra inputkanalen i heltal og skriver // til outputkanalen kun de heltal, der er positive. // Returnerer outputkanalen. func positives ( in <- chan int64 ) <- chan int64 { out := make ( chan int64 ) go func () { // Sløjfen fortsætter indtil kanal ind er lukket for næste := range in { if next > 0 { ud <- næste } } luk ( ud ) }() returner ud }

Ud over CSP eller i forbindelse med kanaliseringsmekanismen giver Go dig også mulighed for at bruge den sædvanlige model for synkroniseret interaktion af tråde gennem delt hukommelse ved at bruge typiske adgangssynkroniseringsværktøjer såsom mutexes . Samtidig advarer sprogspecifikationen dog mod ethvert forsøg på usynkroniseret interaktion af parallelle tråde gennem delt hukommelse, da compileren i mangel af eksplicit synkronisering optimerer dataadgangskoden uden at tage højde for muligheden for samtidig adgang fra forskellige tråde, hvilket kan føre til uventede fejl. For eksempel er skrivning af værdier til globale variabler i en tråd muligvis ikke synlige eller synlige i den forkerte rækkefølge fra en parallel tråd.

Overvej for eksempel programmet nedenfor. Funktionskoden er main()skrevet ud fra den antagelse, at den funktion, der lanceres i goroutinen setup(), vil skabe en struktur af typen T, initialisere den med strengen "hello, world", og derefter tildele en reference til den initialiserede struktur til den globale variabel g. B main()starter en tom sløjfe og venter på, at en gværdi, der ikke er nul, vises. Så snart den vises, main()udskriver en streng fra den struktur, der peges gpå, forudsat at strukturen allerede er initialiseret.

skriv T struct { msg string } varg * T _ func setup () { t : = new ( T ) t . msg = "hej verden" g = t } func main () { go setup () for g == nul { // VIRKER IKKE!!! } udskriv ( g . besked ) }

I virkeligheden er en af ​​to fejl mulig.

  • Hovedtråden kan simpelthen ikke se ændringen i variablen g, og så vil programmet hænge i en endeløs løkke. Dette kan ske, hvis en compiler, der er konfigureret til aggressiv optimering, bestemmer, at den skabte setup()værdi ikke sendes nogen steder, og blot fjerner hele koden for denne funktion som ubetydelig.
  • Hovedtråden vil se, at værdien gikke længere er nul, men værdien vil ikke blive initialiseret på det g.msgtidspunkt, hvor funktionen udføres; print()i dette tilfælde vil programmet udsende en tom streng. Dette kan ske, hvis compileren af ​​optimeringsøjemed fjerner den lokale variabel tog skriver en reference til det oprettede objekt direkte i g.

Den eneste korrekte måde at organisere dataoverførsel gennem delt hukommelse på er at bruge bibliotekssynkroniseringsværktøjer, som garanterer, at alle data skrevet af en af ​​de synkroniserede streams før synkroniseringspunktet garanteres at være tilgængelige i en anden synkroniseret stream efter synkroniseringspunktet.

En funktion ved multithreading i Go er, at en goroutine ikke identificeres på nogen måde og ikke er et sprogobjekt, der kan henvises til, når man kalder funktioner, eller som kan placeres i en container. Følgelig er der ingen midler, der giver dig mulighed for direkte at påvirke udførelsen af ​​en koroutine udefra, såsom at suspendere og derefter starte den, ændre prioritet, vente på færdiggørelsen af ​​en koroutine i en anden og tvangsafbryde henrettelsen. Enhver handling på en goroutine (bortset fra at afslutte hovedprogrammet, som automatisk afslutter alle goroutiner) kan kun udføres gennem rør eller andre synkroniseringsmekanismer. Det følgende er eksempelkode, der starter adskillige goroutiner og venter på, at de fuldføres ved hjælp af WaitGroup-synkroniseringsobjektet fra synkroniseringssystempakken. Dette objekt indeholder en tæller, oprindeligt nul, som kan øges og formindskes, og en Wait()-metode, som får den aktuelle tråd til at pause og vente, indtil tælleren er nulstillet.

func main () { var wg sync . WaitGroup // Opret en ventegruppe. Startværdien af ​​tælleren er 0 logger := log . New ( os . Stdout , "" , 0 ) // log.Logger er en trådsikker outputtype for _ , arg := range os . Args { // Gå gennem alle kommandolinjeargumenter wg . Tilføj ( 1 ) // Øg ventegruppetælleren med én // Kør en goroutine for at behandle arg-parameteren go func ( ordstreng ) { // Forsinket nedsættelse af ventegruppetælleren med én . // Sker når funktionen slutter. udskyde wg . Udført () logger . Println ( prepareWord ( word )) // Udfør bearbejdning og udskriv resultatet }( arg ) } wg . Vent () // Vent indtil tælleren i ventegruppe wg er nul. }

Her, før oprettelsen af ​​hver ny goroutine, øges tælleren for objektet wg med én, og efter færdiggørelsen af ​​goroutinen dekrementeres den med én. Som et resultat vil der i løkken, der starter behandlingen af ​​argumenter, blive tilføjet lige så mange enheder til tælleren, som der er lanceret goroutiner. Når løkken slutter, vil kald af wg.Wait() få hovedprogrammet til at pause. Efterhånden som hver af goroutinerne afsluttes, sænker den wg-tælleren med én, så hovedprogrammets ventetid slutter, når lige så mange goroutiner, som det kørte, er afsluttet. Uden den sidste linje ville hovedprogrammet, efter at have kørt alle goroutinerne, afslutte med det samme og afbryde udførelsen af ​​dem, der ikke havde tid til at udføre.

På trods af tilstedeværelsen af ​​multithreading indbygget i sproget, er ikke alle standardsprogobjekter trådsikre. Så standardkorttypen (visning) er ikke trådsikker. Skaberne af sproget forklarede denne beslutning med effektivitetshensyn, da sikring af sikkerhed for alle sådanne objekter ville føre til yderligere overhead, hvilket langt fra altid er nødvendigt (de samme operationer med kortlægninger kan være en del af større operationer, der allerede er synkroniseret af programmøren , og så vil den ekstra synkronisering kun komplicere og bremse programmet). Fra version 1.9 har synkroniseringsbibliotekspakken, som indeholder understøttelse af parallel behandling, tilføjet den trådsikre sync.Map-type, som kan bruges om nødvendigt. Du kan også være opmærksom på den trådsikre type, der bruges i eksemplet til at vise resultater log.Logger; den bruges i stedet for standard fmt-pakken, hvis funktioner (Printf, Println, og så videre) ikke er trådsikre og vil kræve yderligere synkronisering.

Objektorienteret programmering

Der er ikke noget særligt nøgleord til at erklære en klasse i Go, men metoder kan defineres for enhver navngiven type, inklusive strukturer og basistyper som int , så i OOP-forstand er alle sådanne typer klasser.

skriv newInt int

Metodedefinitionssyntaksen er lånt fra Oberon-2 sproget og adskiller sig fra den sædvanlige funktionsdefinition ved, at efter func nøgleordet erklæres den såkaldte "receiver" ( engelsk  modtager ) i parentes , dvs. det objekt, for hvilket metode kaldes, og typen, som metoden tilhører. Hvorimod modtageren i traditionelle objektsprog er underforstået og har et standardnavn (i C++ eller Java er det "dette", i ObjectPascal er det "selv" osv.), i Go er det specificeret eksplicit, og dets navn kan være enhver gyldig Go-id.

type myType struct { i int } // Her er p modtageren i metoder af typen myType. func ( p * myType ) get () int { return p . i } func ( p * myType ) sæt ( i int ) { p . jeg = jeg }

Der er ingen formel nedarvning af klasser (strukturer) i Go, men der er en teknisk tæt indlejringsmekanisme .  I beskrivelsen af ​​strukturen kan du bruge det såkaldte anonyme felt - et felt, hvor der ikke er angivet et navn, men kun en type. Som et resultat af en sådan beskrivelse vil alle elementer i den indlejrede struktur blive de samme navngivne elementer i den indlejrede struktur.

// Ny struct type type myType2 struct { myType // Anonymt felt giver indlejring af typen myType. // Nu indeholder myType2 i-feltet og metoderne get() og set(int). k int }

I modsætning til klassisk nedarvning involverer inlining ikke polymorf adfærd (et objekt i en indlejringsklasse kan ikke fungere som et objekt i en indlejrbar klasse uden eksplicit typekonvertering).

Det er ikke muligt eksplicit at erklære metoder for en unavngiven type (syntaksen tillader simpelthen ikke at angive typen af ​​modtageren i metoden), men denne begrænsning kan let omgås ved at inline den navngivne type med de nødvendige metoder.

Klassepolymorfi leveres i Go af grænseflademekanismen (svarende til fuldt abstrakte klasser i C++ ). En grænseflade er beskrevet ved hjælp af grænsefladenøgleordet; indeni (i modsætning til klassetypedeklarationer) erklærer beskrivelser de metoder, som grænsefladen giver.

skriv myInterface interface { get () int set ( i int ) }

I Go er der ingen grund til eksplicit at angive, at en type implementerer en bestemt grænseflade. I stedet er reglen, at hver type, der leverer metoder defineret i en grænseflade, kan bruges som en implementering af denne grænseflade. Den ovenfor erklærede type myTypeimplementerer grænsefladen myInterface, selvom dette ikke er udtrykkeligt angivet nogen steder, fordi den indeholder metoder get()og set(), hvis signaturer matcher dem, der er beskrevet i myInterface.

Ligesom klasser kan grænseflader indlejres:

skriv mySecondInterface interface { myInterface // samme som eksplicit erklærer get() int; set(i int) change ( i int ) int }

Her arver mySecondInterface-grænsefladen myInterface-grænsefladen (dvs. den erklærer, at den afslører metoderne inkluderet i myInterface) og erklærer desuden en native metode change().

Selvom det i princippet er muligt at bygge et Go-program ind i et hierarki af grænseflader, som det gøres i andre objektsprog, og endda at simulere nedarvning, betragtes dette som dårlig praksis. Sproget dikterer ikke en hierarkisk, men en kompositorisk tilgang til systemet af klasser og grænseflader. Strukturklasser med denne tilgang kan generelt forblive formelt uafhængige, og grænseflader kombineres ikke i et enkelt hierarki, men skabes til specifikke applikationer, om nødvendigt, indlejring af eksisterende. Den implicitte implementering af grænseflader i Go giver disse mekanismer ekstrem fleksibilitet og et minimum af tekniske vanskeligheder i deres brug.

Denne tilgang til arv er i tråd med nogle praktiske tendenser i moderne programmering. Så i den berømte bog "bande på fire" ( Erich Gamma og andre) om designmønstre , er det især skrevet:

Implementeringsafhængighed kan forårsage problemer, når du forsøger at genbruge en underklasse. Hvis selv et aspekt af den gamle implementering er uegnet til det nye domæne, så skal den overordnede klasse omskrives eller erstattes med noget mere passende. Denne afhængighed begrænser fleksibilitet og genanvendelighed. Problemet kan løses ved kun at arve fra abstrakte klasser, da de normalt ikke har nogen eller minimal implementering.

Der er intet koncept for en virtuel funktion i Go . Polymorfi er tilvejebragt af grænseflader. Hvis en variabel af en almindelig type bruges til at kalde en metode, så er et sådant kald statisk bundet, det vil sige, at den metode, der er defineret for denne særlige type, altid kaldes. Hvis metoden kaldes for en variabel af typen "interface", så er et sådant kald dynamisk bundet, og på udførelsestidspunktet metodevarianten, der er defineret for typen af ​​objektet, der faktisk er tildelt på tidspunktet for kaldet variabel er valgt til lancering.

Dynamisk understøttelse af objektorienteret programmering til Go leveres af GOOP- projektet .

Refleksion

Evnen til at introspektere under kørsel, det vil sige adgang til og behandling af værdier af enhver type og dynamisk justering af de typer data, der behandles, er implementeret i Go ved hjælp af systempakken reflect. Denne pakke giver dig mulighed for at:

  • bestemme typen af ​​enhver værdi;
  • sammenligne to vilkårlige værdier for ækvivalens, inklusive dem, der ikke sammenlignes med standard sprogværktøjer, for eksempel udsnit;
  • arbejde med den samme kode med værdier af enhver type (en type reflect.Valuegiver dig mulighed for at repræsentere en værdi af enhver sprogtype og konvertere den til en af ​​standardtyperne, hvis en sådan konvertering er mulig);
  • ændre eventuelle værdier, hvis en sådan ændring i princippet er mulig (for eksempel ændre en del af en streng);
  • udforske typer, især adgang til strukturfelter og deres deskriptorer, få lister over typemetoder, deres beskrivelser;
  • kalder vilkårlige funktioner og metoder.

Pakken reflectindeholder også mange hjælpeværktøjer til at udføre operationer afhængigt af programmets dynamiske tilstand.

Programmering på lavt niveau

Faciliteter til hukommelsesadgang på lavt niveau er koncentreret i systempakken unsafe. Dets ejendommelighed er, at selvom det ligner en almindelig Go-pakke, implementeres det faktisk af compileren selv. Pakken unsafegiver adgang til den interne repræsentation af data og til "rigtige" hukommelsespointere. Det giver funktioner:

  • unsafe.Sizeof() - argumentet kan være et udtryk af enhver type, funktionen returnerer den faktiske størrelse af operanden i bytes, inklusive ubrugt hukommelse, der kan forekomme i strukturer på grund af justering;
  • unsafe.Alignof() - Argumentet kan være et udtryk af enhver type, funktionen returnerer størrelsen i bytes, i henhold til hvilken operandens typer er justeret i hukommelsen;
  • unsafe.Offsetof() - argumentet skal være et felt af strukturen, funktionen returnerer offset i bytes, hvor dette felt er placeret i strukturen.

Pakken giver også en type unsafe.Pointer, der kan konverteres til en hvilken som helst pointer og kan konverteres til en pointer af enhver type, såvel som en standardtype uintptr , en usigneret heltalsværdi, der er stor nok til at gemme en fuld adresse på den aktuelle platform. Ved at konvertere markøren til unsafe.Pointerog derefter til uintptr, kan du få adressen som et heltal, som du kan anvende aritmetiske operationer på. Ved derefter at konvertere værdien tilbage til unsafe.Pointerog til en pointer til en bestemt type, kan du på denne måde få adgang næsten overalt i adresserummet.

De beskrevne transformationer kan være usikre, så de anbefales at undgå, hvis det er muligt. For det første er der åbenlyse problemer forbundet med fejlagtig adgang til det forkerte hukommelsesområde. En mere subtil pointe er, at på trods af brugen af ​​pakken unsafe, bliver Go-objekter fortsat administreret af hukommelsesadministratoren og skraldeopsamleren. Konvertering af en pointer til et tal sætter denne pointer ud af kontrol, og programmøren kan ikke forvente, at en sådan konverteret pointer forbliver relevant på ubestemt tid. For eksempel at prøve at gemme en markør til et nyt typeobjekt som Тdette:

pT := uintptr ( usikker . Pointer ( ny ( T ))) // FORKERT!

vil få objektet til at blive oprettet, markøren til det konverteret til et tal (som vil blive tildelt til pT). Det har dog pTen heltalstype og anses ikke af skraldeopsamleren for at være en pegepind til et oprettet objekt, så efter at handlingen er fuldført, vil hukommelsesstyringssystemet betragte dette objekt som ubrugt. Det vil sige, at den kan fjernes af skraldemanden, hvorefter den konverterede pointer pTbliver ugyldig. Dette kan ske til enhver tid, både umiddelbart efter afslutningen af ​​operationen, og efter mange timers programdrift, således at fejlen kommer til udtryk i tilfældige programnedbrud, hvis årsag vil være ekstremt svær at identificere. Og når du bruger en affaldsopsamler i bevægelse [* 1] , kan markøren konverteret til et tal blive irrelevant, selv når objektet endnu ikke er blevet fjernet fra hukommelsen.

Da Go-specifikationen ikke giver præcise indikationer af, i hvilket omfang en programmør kan forvente at holde en pointer konverteret til et tal ajour, er der en anbefaling: at holde sådanne konverteringer på et minimum og organisere dem, så konverteringen af den originale pointer, dens ændringer og tilbagekonvertering er inden for en sproginstruktion, og når du kalder biblioteksfunktioner, der returnerer en adresse i form af uintptr, skal du straks konvertere deres resultat til unsafe.Pointerfor at bevare garantien for, at markøren ikke går tabt.

Pakken unsafebruges sjældent direkte i applikationsprogrammering, men den bruges aktivt i pakkerne reflect, os, syscall, context, netog nogle andre.

Interface med kode på andre sprog

Der er adskillige eksterne værktøjer, der giver udenlandske funktionsgrænseflader (FFI) til Go-programmer. Cgo -værktøjet kan bruges til at interagere med ekstern C -kode (eller have en C-kompatibel grænseflade) . Det kaldes automatisk, når compileren behandler et korrekt skrevet Go-modul, og sikrer oprettelsen af ​​en midlertidig Go-indpakningspakke, der indeholder erklæringer af alle nødvendige typer og funktioner. I C-funktionsopkald er du ofte nødt til at ty til pakkefaciliteter , hovedsageligt ved hjælp af . Et mere kraftfuldt værktøj er SWIG [16] , som giver mere avancerede funktioner, såsom integration med C++ klasser . unsafeunsafe.Pointer

Brugergrænseflade

Go-standardbiblioteket understøtter opbygning af konsolapplikationer og webbaserede serverapplikationer , men der er ingen standardværktøjer til at bygge GUI'er i klientapplikationer. Dette hul udfyldes af tredjeparts-indpakninger til populære UI- rammer såsom GTK+ og Qt , under Windows kan du bruge de grafiske WinAPI -værktøjer ved at få adgang til dem gennem pakken syscall, men alle disse metoder er ret besværlige. Der er også flere udviklinger af UI-rammer i selve Go, men ingen af ​​disse projekter har nået niveauet for industriel anvendelighed. I 2015 på GopherCon-konferencen i Denver besvarede en af ​​skaberne af sproget, Robert Grismer, spørgsmål, var enig i, at Go har brug for en UI-pakke, men bemærkede, at en sådan pakke skulle være universel, kraftfuld og multi-platform, hvilket gør dens udvikling lang og vanskelig proces. Spørgsmålet om implementering af en klient-GUI i Go er stadig åbent.

Kritik

På grund af sprogets ungdom er dets kritik hovedsageligt koncentreret i internetartikler, anmeldelser og fora.

Mangel på muligheder

Meget af kritikken af ​​sproget fokuserer på dets mangel på visse populære funktioner fra andre sprog. Blandt dem [17] [18] [19] [20] :

Som nævnt ovenfor skyldes manglen på en række funktioner tilgængelige på andre populære sprog udviklernes bevidste valg, som mener, at sådanne funktioner enten hindrer effektiv kompilering eller provokerer programmøren til at lave fejl eller skabe ineffektive eller "dårligt" med hensyn til kodevedligeholdelse, eller have andre uønskede bivirkninger.

Arkitektur

  • Ingen opregnede typer. Grupper af konstanter bruges i stedet, men alle værdikonstanter er faktisk heltal, disse grupper er ikke syntaktisk kombineret, og compileren har ingen kontrol over deres brug. Det er ikke muligt at beskrive en type, der er knyttet til en opregning, for eksempel et array, der indeholder et element for hvert element i en opregning (som i Pascal er beskrevet af den slags konstruktion type EnumArray = array[EnumType] of ElementType), skab en løkke over opregningen, compileren kan ikke kontrollere fuldstændigheden af ​​listen over alternativer i konstruktionen switch, når værdien bruges som en vælgeropregning.
  • Utilstrækkelighed af indbyggede containerdatatyper.
    Containere, der er indbygget i sproget, er begrænset til arrays og kortlægninger. Containere implementeret ved hjælp af selve sproget (inklusive dem, der er inkluderet i standardbiblioteket) er ikke typesikre på grund af tvungen brug af elementer af typen i dem interface{}, og desuden kan de ikke omgås ved hjælp af for range.
  • Fraværet af en eksplicit indikation af implementeringen af ​​grænsefladen efter typen gør det vanskeligt at forstå koden, ændre den og refaktorisere den . Compileren kan ikke automatisk kontrollere typen i forhold til de implementerede grænseflader. Det er også muligt (omend usandsynligt) at "ved et uheld implementere" en grænseflade, hvor metoderne af en type har samme signaturer som grænsefladens metoder, men ikke i bund og grund er en implementering af den adfærd, som grænsefladen repræsenterer.
  • Opgivelsen af ​​strukturel undtagelseshåndtering til fordel for returnerende fejl gør det umuligt at koncentrere fejlhåndteringen ét sted, fejltjek roder koden og gør den svær at forstå. Derudover er mekanismen til håndtering af paniktilstanden i det væsentlige ikke forskellig fra undtagelsesbehandlerne i try-catch. Desuden, i modsætning til deres egne anbefalinger, bruger forfatterne af sproget panikgenerering og -behandling til at håndtere logiske fejl i standardbiblioteket.
  • Strukturfeltmærker styres ikke af compileren.
    De tags, der angiver yderligere egenskaber for strukturfelter, er blot strenge, der behandles dynamisk, der er ikke engang de enkleste syntaktiske begrænsninger på deres format. Dette gøres for ikke at begrænse udvikleren i brugen af ​​tags, men i praksis fører det til, at der ikke kan opdages en fejl ved at skrive et tag på kompileringsstadiet.

"Fundgruber" (mislykket implementering af nogle midler)

Kritikere påpeger, at nogle funktioner i Go er implementeret i form af den enkleste eller mest effektive implementering, men ikke opfylder " princippet om mindste overraskelse ": deres adfærd adskiller sig fra, hvad programmøren ville forvente baseret på intuition og tidligere erfaringer. Sådanne funktioner kræver øget opmærksomhed fra programmøren, gør det vanskeligt at lære og skifte fra andre sprog.

  • I en samlingsløkke er værdivariablen en kopi, ikke en reference.
    I en løkke som " for index, value := range collection" er variablen valueen kopi af det aktuelle element. Operationen med at tildele en ny værdi til denne variabel er tilgængelig, men ændrer mod forventning ikke det aktuelle element i samlingen.
  • Null-grænsefladen er ikke lig med grænsefladen for null-objektet.
    En værdi af typen "interface" er en struktur af to referencer - til metodetabellen og til selve objektet. Ved nul-grænsefladen er begge felter ens nil. En grænseflade, der peger på et nul-objekt, har sin første reference udfyldt; det er ikke lig med nul-grænsefladen, selvom der normalt ikke er nogen forskel mellem de to med hensyn til programlogik [21] . Dette fører til uventede effekter og gør det vanskeligt at kontrollere rigtigheden af ​​værdierne for grænsefladetyper:
type I -grænseflade { f () } type T struct {} func ( T ) f () { ... } // Type T implementerer interface I. main () { var t * T = nul // t er en nul pointer til at skrive T. var i I = t // Skriv en nul pointer til T i en grænsefladevariabel. hvis jeg != nul { //! Overraskelse. Selvom jeg fik tildelt en nul-pointer, i != nul i . f () // Dette opkald vil ske og forårsage panik. } ... } Selvom inul-markøren til objektet blev skrevet til variablen, er selve værdien iikke tom, og sammenligningen i != nilgiver et positivt resultat. For at sikre, at en grænsefladevariabel peger på et gyldigt objekt, skal du bruge refleksion, hvilket komplicerer koden betydeligt: hvis jeg != nul && ! reflektere . ValueOf ( i ). isnil () { ...
  • Inhomogen opgavesemantik selv på nært beslægtede typer.
    Indbyggede typer og strukturer tildeles efter værdi, grænseflader tildeles ved reference. Arrays med en statisk erklæret længde tildeles efter værdi, arrays uden en erklæret længde og visning tildeles ved reference. Faktisk er valget af opgavesemantik for en type bestemt af, hvordan værdierne af denne type er allokeret i hukommelsen, det vil sige, at sproget er implementeringsdefineret.
  • Forskellig adfærd af operationer på arrays og skiver under forskellige forhold.
    For eksempel kan en standardfunktion, append()der tilføjer elementer til et array, oprette og returnere et nyt array, eller den kan tilføje og returnere en eksisterende, afhængigt af om der er nok ledig plads i den til at tilføje elementer. I det første tilfælde vil efterfølgende ændringer af det resulterende array ikke påvirke originalen, i det andet tilfælde vil de blive afspejlet i det. Denne adfærd tvinger den konstante brug af kopifunktionen copy().

Andre funktioner

Ofte kritiseres mekanismen for automatiske semikoloner, på grund af hvilke nogle former for skriveudsagn, funktionskald og lister bliver forkerte. I en kommentar til denne beslutning bemærker sprogets forfattere [9] , at det sammen med tilstedeværelsen af ​​en kodeformater i det officielle værktøjssæt gofmtførte til fikseringen af ​​en ret stiv standard for kodning i Go. Det er næppe muligt at skabe en standard for at skrive kode, der passer til alle; introduktionen til sproget af en funktion, der i sig selv sætter en sådan standard, forener programmernes udseende og eliminerer principløse konflikter på grund af formatering, hvilket er en positiv faktor for gruppeudvikling og vedligeholdelse af software.

Fordeling og perspektiver

Gos popularitet er vokset i de seneste år: Fra 2014 til 2020 er Go steget fra 65. pladsen til 11. pladsen på TIOBE- ranglisten, ratingværdien for august 2020 er 1,43 %. Ifølge resultaterne af en dou.ua-undersøgelse [22] blev Go-sproget i 2018 det niende på listen over de mest brugte og det sjette på listen over sprog, som udviklere giver personlig præference til.

Siden den første offentlige udgivelse i 2012 har brugen af ​​sproget været støt stigende. Listen over virksomheder, der bruger sproget i industriel udvikling, offentliggjort på Go-projektets hjemmeside, omfatter flere dusin navne. Et stort udvalg af biblioteker til forskellige formål er blevet akkumuleret. Udgivelsen af ​​version 2.0 var planlagt til 2019, men arbejdet blev forsinket og er stadig i gang i anden halvdel af 2022. En række nye funktioner forventes at dukke op, herunder generiske og speciel syntaks for at forenkle fejlhåndtering, hvis fravær er en af ​​de mest almindelige klager fra kritikere af sproget .

RoadRunner (Application Server) webserveren blev udviklet i Golang , som gør det muligt for webapplikationer at opnå en anmodning-svar-hastighed på 10-20 ms i stedet for de traditionelle 200 ms. Denne webservice er planlagt til at blive inkluderet i populære rammer såsom Yii .

Sammen med C ++ bruges Golang til at udvikle mikrotjenester, som giver dig mulighed for at "indlæse" multi-processor platforme med arbejde. Du kan interagere med en mikrotjeneste ved hjælp af REST , og PHP -sproget er fantastisk til dette.

Spiral Framework blev udviklet ved hjælp af PHP og Golang. [23]

Versioner

Nummererings- og versionskompatibilitetsprincipper

Der er kun én større version af selve Go-sproget, version 1. Versioner af Go-udviklingsmiljøet (compiler, værktøjer og standardbiblioteker) er nummereret enten tocifret ("<sprogversion>.<større udgivelse>") eller trecifret ("<sprogversion>.< større udgivelse>.<mindre udgivelse>") til systemet. Udgivelsen af ​​en ny "tocifret" version afslutter automatisk understøttelsen af ​​den tidligere "tocifrede" version. "Trecifrede" versioner frigives for at rette rapporterede fejl og sikkerhedsproblemer; sikkerhedsrettelser i sådanne versioner kan påvirke de sidste to "to-cifrede" versioner [24] .

Forfatterne erklærede [25] ønsket om så vidt muligt at bevare bagudkompatibilitet inden for hovedversionen af ​​sproget. Det betyder, at før udgivelsen af ​​Go 2 vil næsten ethvert program, der er oprettet i Go 1-miljøet, kompileres korrekt i enhver efterfølgende version af Go 1.x og køre uden fejl. Undtagelser er mulige, men de er få. Binær kompatibilitet mellem udgivelser er dog ikke garanteret, så et program skal være fuldstændigt omkompileret, når man flytter til en senere udgivelse af Go.

Gå 1

Siden marts 2012, hvor Go 1 blev introduceret, er følgende hovedversioner blevet frigivet:

  • go 1 - 28. marts 2012 - Første officielle udgivelse; biblioteker rettet, syntaksændringer foretaget.
  • go 1.1 - 13. maj 2013 - heltalsdivision med nul blev en syntaksfejl, metodeværdier blev introduceret - metodelukninger med en given kildeværdi, i nogle tilfælde blev brugen af ​​retur valgfri; implementeringen har lov til at vælge mellem 32-bit og 64-bit repræsentation af standard heltal-typen, ændringer i Unicode-understøttelse.
  • go 1.2 - 1. december 2013 - ethvert forsøg på at få adgang til nul-markøren vil med garanti forårsage panik, tre-indeks skiver introduceres. Forbedringer til Unicode.
  • go 1.3 - 18. juni 2014 - ændret hukommelsesallokeringsmodellen; fjernet understøttelse af Windows 2000-platformen, tilføjet DragonFly BSD, FreeBSD, NetBSD, OpenBSD, Plan 9, Solaris.
  • go 1.4 - 10. december 2014 - konstruktionen af ​​løkken " for interval x { ... } " er tilladt (en løkke gennem en samling uden brug af variabler), dobbelt automatisk dereferencing er forbudt, når en metode kaldes (hvis x ** T er en dobbelt pointer til at skrive T og kalder derefter metoden for x i form af xm() - forbudt); understøttelse af Android, NaCl på ARM, Plan9 på AMD64-platforme er blevet tilføjet til implementeringen.
  • go 1.5 - 19. august 2015 - i notationen af ​​kortlitteraler er angivelsen af ​​typen af ​​hvert element gjort valgfri, i implementeringen er runtime og compiler fuldstændig omskrevet i Go og assembler, C-sproget bruges ikke længere.
  • go 1.6 - 17. februar 2016 - ingen sprogændringer, miljø porteret til Linux på 64-bit MIPS, Android på 32-bit x86 (android/386), ændringer i værktøjssæt.
  • go 1.7 - 16. august 2016 - Reduceret kompileringstid og størrelse af binære filer, øget hastighed og tilføjet kontekstpakken til standardbiblioteket.
  • go 1.8 - 7. april 2017 - den indbyggede hukommelse skraldeopsamler er blevet accelereret, "http"-modulet har fået mulighed for blødt stop, understøttelse af processorer med MIPS-arkitekturen (32-bit) er blevet tilføjet. Der er foretaget rettelser til en række pakker og hjælpeprogrammer.
  • go 1.9 - 24. august 2017 - aliaser af typenavne blev tilføjet til sproget, nogle punkter ved brug af flydende komma-operationer blev tydeliggjort, værktøjer blev optimeret, biblioteker blev tilføjet, især den trådsikre korttype.
  • go 1.10 - 16. februar 2018 - to præciseringer blev lavet til sproget, som faktisk legitimerede eksisterende implementeringer, resten af ​​ændringerne vedrører biblioteker og værktøjer. Tre "trecifrede" udgivelser 1.10.1 - 1.10.3 er blevet frigivet, der indeholder rettelser til opdagede fejl.
  • go 1.11 - 24. august 2018 - tilføjet (som eksperimentel) understøttelse af moduler (ny pakkeversionsmekanisme og afhængighedsstyring), samt muligheden for at kompilere til WebAssembly , forbedret understøttelse af ARM-processorer, ændringer blev foretaget i værktøjssættet og bibliotekerne (især tilføjet pakke syscall/js; compileren styrer nu korrekt brugen af ​​variabler erklæret i switch-sætninger med typekontrol).
  • go 1.12 - 25. februar 2019 - rettelser i biblioteker og hjælpeprogrammer. Annonceret som den sidste udgivelse, der bibeholder understøttelse af FreeBSD 10.X og macOS 10.10. Tilføjet cgo-understøttelse på linux/ppc64-platformen. Tilføjet understøttelse af AIX OS . Indtil august 2019 blev ni patch-udgivelser frigivet som en del af denne udgivelse, der rettede forskellige fejl.
  • go 1.13 - 3. september 2019 - nye numeriske bogstaver blev tilføjet til sproget: binære og oktale heltal, hexadecimalt flydende komma (sidstnævnte skal indeholde eksponenten, adskilt af p- eller P-symbolet); tilladt brugen af ​​understregninger til at adskille cifre i tal; bitvis skiftoperation er tilladt for heltal med fortegn; tilføjet understøttelse af Android 10; understøttelse af ældre versioner er afbrudt på en række platforme.
  • go 1.14 - 25. februar 2020 - Definitionen af ​​at inkludere grænseflader er blevet udvidet: det er nu tilladt at inkludere flere grænseflader, der har metoder af samme navn med identiske signaturer. Ændringer i biblioteker, runtime-miljø, værktøjer.
  • go 1.15 - 11. august 2020 - fjernede understøttelse af 32-bit OS-varianter på Darwin-kernen, forbedret linkerydeevne, tilføjet valgfri Spectre-sårbarhedsreduktion , tilføjet nye advarsler om go vet-værktøj. Der var ingen sprogændringer i denne udgivelse. Ved udgangen af ​​november 2020 var der fem mindre udgivelser, der fikser fejl og fikser sikkerhedssårbarheder.
  • go 1.16 - 16. februar 2021 - Tilføjet understøttelse af 64-bit ARM under macOS og NetBSD, MIPS64 under OpenBSD, forbedret implementering for en række arkitekturer, inklusive RISC-V. Understøttelse af moduler er aktiveret som standard, muligheden for eksplicit at angive versioner er blevet tilføjet til build-kommandoparametrene. Der er ingen sprogændringer. Der er foretaget ændringer i biblioteker, især er der tilføjet en pakke, embedder implementerer muligheden for at få adgang til filer indbygget i det eksekverbare modul. Fra juni 2021 er fem mindre udgivelser blevet frigivet.

Go 2.0

Udviklingsfremskridt Siden 2017 har forberedelserne været i gang til udgivelsen af ​​den næste basisversion af sproget, som har symbolet "Go 2.0" [26] . Indsamlingen af ​​kommentarer til den aktuelle version og forslag til transformationer, samlet på projektets wiki-side [27] . Oprindeligt blev det antaget, at forberedelsesprocessen ville tage "ca. to år", og nogle af de nye elementer i sproget ville blive inkluderet i de næste udgivelser af Go 1-versionen (selvfølgelig kun dem, der ikke krænker bagudkompatibilitet ). [26] Fra april 2021 er version 2.0 endnu ikke klar, nogle af de planlagte ændringer er i design- og implementeringsfasen. Ifølge planerne skitseret i projektbloggen [28] vil arbejdet med implementeringen af ​​de planlagte ændringer fortsætte i mindst 2021. Foreslåede innovationer Blandt de grundlæggende innovationer er eksplicit erklærede konstante værdier, en ny fejlhåndteringsmekanisme og generiske programmeringsværktøjer. Innovationsprojekter er tilgængelige online. Den 28. august 2018 blev en video tidligere præsenteret på Gophercon 2018-konferencen offentliggjort på den officielle udviklerblog , som demonstrerer udkast til versioner af det nye fejlhåndteringsdesign og generiske funktionsmekanisme. Der er også planlagt mange mindre bemærkelsesværdige, men meget væsentlige ændringer, [29] såsom at udvide reglerne for tilladeligheden af ​​tegn for identifikatorer i ikke-latinske alfabeter, tillade skiftoperationer for fortegnede heltal, brug af understregningen som en adskillelse af grupper af tusinder i tal, binære bogstaver . De fleste af dem er allerede implementeret og tilgængelige i de nyeste versioner af Go 1. Fejl ved behandling Flere muligheder for at ændre fejlhåndteringsmekanismen blev overvejet, især et design med en separat fejlbehandler (" Error Handling - Draft Design "). Den sidste variant for juli 2019 er beskrevet i artiklen " Forslag: En indbygget Go-fejlkontrolfunktion, prøv ". Denne mulighed er den mest minimalistiske og involverer kun tilføjelse af én indbygget funktion try(), der behandler resultatet af et funktionskald. Dets brug er illustreret af pseudokoden nedenfor. func f ( )( r1 type_1 , , rn type_n , err error ) { // Testet funktion // Returnerer n+1 resultater: r1... rn, err of type error. } func g ( )( , err error ) { // Kald funktion f() med fejlkontrol: x1 , x2 , xn = try ( f ( )) // Brug indbygget try: // if f( ) returnerede ikke-nul i det sidste resultat, så vil g() automatisk afslutte, // returnerer den samme værdi i ITS sidste resultat. } func t ( )( , err error ) { // Svarer til g() uden at bruge den nye syntaks: t1 , t2 , tn , te := f ( ) // Kald f() med resultater gemt i midlertidig variabler. if te != nil { // Tjek returkoden for lighed nil err = te // Hvis returkoden ikke er nul, så skrives den til det sidste resultat af t(), returnerer // hvorefter t() afsluttes straks. } // Hvis der ikke var nogen fejl, x1 , x2 , xn = t1 , t2 , tn // … variabler x1…xn får deres værdier // og udførelsen af ​​t() fortsætter. } Det vil sige, try()at den blot giver et fejltjek i opkaldet til den funktion, der kontrolleres, og en øjeblikkelig retur fra den aktuelle funktion med samme fejl. Du kan bruge mekanismen til at håndtere en fejl, før du vender tilbage fra den aktuelle funktion defer. Brugen try()kræver, at både den funktion, der kontrolleres, og den funktion, den kaldes i, skal have den sidste returværdi af type error. Derfor kan du for eksempel ikke main()bruge try(); på øverste niveau skal alle fejl håndteres eksplicit. Denne fejlhåndteringsmekanisme skulle være inkluderet i Go 1.14 , men dette blev ikke gjort. Implementeringsdatoer er ikke angivet. Generisk kode I slutningen af ​​2018 blev et udkast til implementering af generiske typer og funktioner i Go præsenteret [30] . Den 9. september 2020 blev et revideret design offentliggjort [31] , hvor funktioner, typer og funktionsparametre kan parameteriseres af parametertyper , som igen er styret af begrænsninger . // Stringer er en begrænsningsgrænseflade, der kræver typen for at implementere // en strengmetode, der returnerer en strengværdi. type Stringer interface { String () string } // Funktionen modtager som input et array af værdier af enhver type, der implementerer String-metoden, og returnerer // den tilsvarende array af strenge opnået ved at kalde String-metoden for hvert element i input-arrayet. func Stringify [ T Stringer ] ( s [] T ) [] string { // typeparameteren T, underlagt Stringer-begrænsningen, // er værditypen for matrixparameteren s. ret = lav ([] streng , len ( s )) for i , v := område s { ret [ i ] = v . String () } return ret } ... v := make ([] MyType ) ... // For at kalde en generisk funktion skal du angive en specifik type s := Stringify [ String ]( v ) Her Stringifyindeholder funktionen en typeparameter T, som bruges i beskrivelsen af ​​en almindelig parameter s. For at kalde en sådan funktion, som vist i eksemplet, skal du i opkaldet angive den specifikke type, den kaldes for. Stringeri denne beskrivelse er det en begrænsning , der kræver, at MyType implementerer en Stringparameterløs metode, der returnerer en strengværdi. Dette gør det muligt for compileren at " " behandle udtrykket korrekt v.String(). Implementeringen af ​​den generiske kode annonceres i version 1.18, der er planlagt til august 2021. [28]

Implementeringer

Der er i øjeblikket to hoved Go-compilere:

  • gc  er det generiske navn for det officielle sæt af udviklingsværktøjer, der vedligeholdes af sprogudviklingsteamet. Det inkluderede oprindeligt 6g (til amd64), 8g (til x86), 5g (til ARM) compilere og relaterede værktøjer, og blev skrevet i C ved hjælp af yacc / Bison til parseren. I version 1.5 blev al C-kode omskrevet i Go og assembler, og individuelle compilere blev erstattet med en single go tool compile .
Understøttet til FreeBSD , OpenBSD , Linux , macOS , Windows , DragonFly BSD , Plan 9 , Solaris , Android , AIX : binære distributioner er tilgængelige for nuværende versioner af FreeBSD , Linux , macOS , Windows , kompilering fra kilde er påkrævet for andre platforme. Udviklere understøtter en begrænset liste over platformsversioner i nye udgivelser af compileren, undtagen fra listen over understøttede versioner, der anses for at være forældede på udgivelsestidspunktet. For eksempel understøtter gc 1.12 Windows 7 og Server 2008R eller nyere.
  • gccgo  er en Go-kompiler med en klientside skrevet i C++ og en rekursiv parser kombineret med standard GCC-backend [32] . Go-support har været tilgængelig i GCC siden version 4.6 [33] . De fleste af uoverensstemmelserne med gc-kompileren er relateret til runtime-biblioteket og er ikke synlige for Go-programmer. [34] 8.1-udgivelsen af ​​gcc understøtter alle sprogændringer op til version 1.10.1 og integrerer en samtidig skraldeopsamler. [35] Tråde (go-procedurer) implementeres i gccgo via OS-tråde, som et resultat af hvilke programmer, der aktivt bruger parallel computing, kan føre til væsentligt højere overheadomkostninger. Understøttelse af lette streams er mulig ved hjælp af guldlinkeren, men den er ikke tilgængelig på alle platforme.

Der er også projekter:

  • llgo  er et lag til at kompilere Go ind i llvm , skrevet i go selv (var under udvikling indtil 2014) [36] [37] .
  • gollvm  er et projekt til kompilering Gå gennem LLVM -kompilersystemet , udviklet af Google. Bruger C++-parser "gofrontend" fra GCCGO og konverter fra gofrontend-visning til LLVM IR [38] [39]
  • SSA-fortolker  er en fortolker, der giver dig mulighed for at køre go-programmer [40] .
  • TinyGo er en Go-compiler, der har til formål at skabe kompakte eksekverbare filer til mikrocontrollere og WebAssembly ved hjælp af LLVM .

Udviklingsværktøjer

Go-udviklingsmiljøet indeholder flere kommandolinjeværktøjer: go-værktøjet, som leverer kompilering, test og pakkehåndtering, og hjælpeværktøjerne godoc og gofmt, designet til henholdsvis at dokumentere programmer og formatere kildekode i henhold til standardregler. For at få vist en komplet liste over værktøjer skal du kalde værktøjet go uden at angive argumenter. gdb-debuggeren kan bruges til at fejlsøge programmer. Uafhængige udviklere leverer et stort antal værktøjer og biblioteker designet til at understøtte udviklingsprocessen, primært for at lette kodeanalyse, test og fejlfinding.

I øjeblikket er to IDE'er tilgængelige, som oprindeligt er fokuseret på Go-sproget - dette er det proprietære GoLand [1] (udviklet af JetBrains på IntelliJ-platformen) og den gratis LiteIDE [2] (tidligere hed projektet GoLangIDE). LiteIDE er en lille shell skrevet i C++ ved hjælp af Qt . Giver dig mulighed for at kompilere, fejlrette, formatere kode, køre værktøjer. Editoren understøtter syntaksfremhævning og autofuldførelse.

Go understøttes også af plugins i de universelle IDE'er Eclipse, NetBeans, IntelliJ, Komodo, CodeBox IDE, Visual Studio, Zeus og andre. Automatisk fremhævning, autofuldførelse af Go-kode og kørsel af kompilering og kodebehandlingsværktøjer er implementeret som plugins til mere end to dusin almindelige teksteditorer til forskellige platforme, herunder Emacs, Vim, Notepad++, jEdit.

Eksempler

Nedenfor er et eksempel på "Hej, verden!" på Go-sproget.

hovedpakke _ importer "fmt" func main () { fmt . println ( "Hej, verden!" ) }

Et eksempel på implementering af Unix echo kommandoen :

hovedpakke _ import ( "os" "flag" // kommandolinjeparser ) var omitNewLine = flag . Bool ( "n" , falsk , "udskriv ikke ny linje" ) const ( Mellemrum = " " NewLine = "\n" ) func main () { flag . Parse () // Scan argumentlisten og sæt flag var s string for i := 0 ; i < flag . Narg (); i ++ { if i > 0 { s += Mellemrum } s += flag . Arg ( i ) } if ! * udeladNewLine { s += NewLine } os . Stdout . WriteString ( s ) }

Noter

Kommentarer
  1. Fra og med 2019 bruger ingen implementering af Go en bevægelig skraldemand.
Kilder
  1. Er Go et objektorienteret sprog? . - "Selvom Go har typer og metoder og tillader en objektorienteret programmeringsstil, er der ikke noget typehierarki." Hentet 13. april 2019. Arkiveret fra originalen 3. maj 2020.
  2. Go: kode, der vokser med ynde . - "Go er objektorienteret, men ikke på den sædvanlige måde." Hentet 24. juni 2018. Arkiveret fra originalen 18. juni 2022.
  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-sprog . Hentet 28. september 2017. Arkiveret fra originalen 18. januar 2010.
  9. 1 2 3 4 5 6 Sprogdesign FAQ . Hentet 11. november 2013. Arkiveret fra originalen 7. januar 2019.
  10. Kom godt i gang - Go-programmeringssproget . Hentet 11. november 2009. Arkiveret fra originalen 20. marts 2012.
  11. 1 2 Rapportering af en navnekonflikt i fejlsporingen . Hentet 19. oktober 2017. Arkiveret fra originalen 23. februar 2018.
  12. 1 2 3 Gå til Google: Language Design in the Service of Software Engineering . talks.golang.org. Hentet 19. september 2017. Arkiveret fra originalen 25. januar 2021.
  13. Rob Pike. Go-programmeringssproget. golang.org, 30/10/2009. . Hentet 3. november 2018. Arkiveret fra originalen 29. august 2017.
  14. når det m[-1]betyder det sidste element i arrayet, m[-2] er det det andet fra slutningen, og så videre
  15. Andrew Gerrand. Udskyd, panik og genvind på GoBlog . Hentet 19. marts 2016. Arkiveret fra originalen 20. april 2014.
  16. SWIG . Hentet 27. november 2018. Arkiveret fra originalen 28. november 2018.
  17. Yager, Will Why Go er ikke godt . Hentet 4. november 2018. Arkiveret fra originalen 16. juli 2019.
  18. Elbre, Egon Resumé af Go Generics-diskussioner . Hentet 4. november 2018. Arkiveret fra originalen 15. juli 2019.
  19. Dobronszki, Janos Everyday Hassles in Go . Hentet 4. november 2018. Arkiveret fra originalen 10. april 2019.
  20. Fitzpatrick, Brad Go: 90 % perfekt, 100 % af tiden . Hentet 28. januar 2016. Arkiveret fra originalen 3. februar 2019.
  21. Donovan, 2016 , s. 224-225.
  22. Rangering af programmeringssprog 2018: Go og TypeScript kom ind i de store ligaer, Kotlin bør tages seriøst  (russisk) , DOW . Arkiveret fra originalen den 4. august 2020. Hentet 29. juli 2018.
  23. Spiralramme . Hentet 23. maj 2020. Arkiveret fra originalen 13. maj 2020.
  24. https://golang.org/doc/devel/release.html Arkiveret 17. februar 2017 på Wayback Machine Go-versionen.
  25. https://golang.org/doc/go1compat Arkiveret 2. oktober 2017 på Wayback Machine Go 1 og fremtidige udgivelser af Go.
  26. 1 2 Toward Go 2 - The Go Blog . blog.golang.org. Hentet 29. juli 2018. Arkiveret fra originalen 26. juni 2018.
  27. golang/  go . GitHub. Hentet 29. juli 2018. Arkiveret fra originalen 29. august 2018.
  28. 1 2 Russ Cox, "Eleven Years of Go" . Hentet 26. november 2020. Arkiveret fra originalen 27. november 2020.
  29. Go2 Her kommer vi! . Hentet 6. december 2018. Arkiveret fra originalen 1. december 2018.
  30. ↑ Kontrakter - Udkast til design  . go.googlesource.com. Hentet 11. oktober 2018. Arkiveret fra originalen 11. oktober 2018.
  31. https://go.googlesource.com/proposal/+/refs/heads/master/design/go2draft-type-parameters.md Arkiveret 23. juni 2020 på Wayback Machine Type Parameters - Draft Design
  32. Go FAQ: Implementering . Hentet 11. november 2013. Arkiveret fra originalen 7. januar 2019.
  33. https://gcc.gnu.org/gcc-4.6/changes.html Arkiveret 2. december 2013 på Wayback Machine "Support for Go-programmeringssproget er blevet tilføjet til GCC."
  34. Opsætning og brug af gccgo - The Go Programming Language . golang.org. Hentet 23. november 2018. Arkiveret fra originalen 23. november 2018.
  35. GCC 8 Release Series - Ændringer, nye funktioner og rettelser - GNU Project - Free Software Foundation (FSF  ) . gcc.gnu.org. Hentet 23. november 2018. Arkiveret fra originalen 29. november 2018.
  36. go-llvm Arkiveret 11. september 2014 på Wayback Machine ; flyttet til llvm-mirror/llgo Arkiveret 11. juni 2018 på Wayback Machine
  37. Arkiveret kopi . Hentet 2. november 2018. Arkiveret fra originalen 22. marts 2017.
  38. gollvm - Git hos Google . Hentet 2. november 2018. Arkiveret fra originalen 8. december 2018.
  39. Gollvm: Google Working On LLVM-baseret Go Compiler  , Phoronix (29. maj 2017) . Arkiveret fra originalen den 12. oktober 2018. Hentet 2. november 2018.
  40. interp - GoDoc . Hentet 2. november 2018. Arkiveret fra originalen 29. maj 2019.

Litteratur

  • Donovan, Alan A. A., Kernighan, Brian, W. Go-programmeringssproget = Go-programmeringssproget. - M . : LLC "I.D. Williams", 2016. - S. 432. - ISBN 978-5-8459-2051-5 .
  • Slagter M., Farina M. Gå i praksis. - " DMK Press ", 2017. - S. 374. - ISBN 978-5-97060-477-9 .
  • Mark Summerfield. Gå til programmering. Udvikling af applikationer fra det XXI århundrede. - " DMK Press ", 2013. - S. 580. - ISBN 978-5-94074-854-0 .

Links