Gopher Programmering i Go

Oprettet: 2015-12-31. Sidst opdateret: 2021-07-21

Velkommen

“Programmering i Go” er en letlæselig og hurtig reference til programmeringssproget Go på dansk.

Bogen er et “levende” dokument der vil fortsætte med at blive udviklet og opdateret med mere brugbar information og eksempler efterhånden som det bliver muligt og tilgængeligt.

Sådan kan du bidrage

Du kan hjælpe med at gøre denne bog til en af de bedste danske bøger om programmering i Go.

Tilføj sektioner til sections mappen eller redigér eksisterende sektioner. Tilføj derefter nyoprettede sektioner til filen sections.txt, der hvor sektionen hører til. “Make” vil automatisk skanne sections.txt og tilføje filen derefter.

Filnavnene skal være på engelsk selvom indholdet er på dansk.

Hvis du ønsker det, kan du efterlade dit navn (hjemmeside eller andet relevant information) i CREDITS.md filen.

Vigtigt: Du skal ikke redigere i HTML filerne. De bliver automatisk genereret med Pandoc.

Bogen kan læses på https://unixsheikh.com/programmering-i-go/

Introduktion

Programmeringssproget “Go”, også kaldet “Golang”, er et programmeringssprog der blev udviklet hos Google i 2007 af Robert Griesemer, Rob Pike, og Ken Thompson. I 2009 havde google allerede taget det i produktion, og det blev efterfølgende annonceret og udgivet som Open Source.

Udviklingen af Go startede med at Robert Griesemer, Rob Pike og Ken Thompson den 21 september 2007 sad og drøftede nogle af de problemer de havde med bl.a. C++ og andre programmeringssprog samt den meget lange kompileringstid der fulgte med i disse sprog.

De drøftede hvad de ville gøre anderledes hvis de skulle designe et nyt programmeringssprog, og en af dem tog nogle notater på en whiteboard. Efter et par dage fremstod der en klar idé og plan om hvordan det skulle gøres. Design af Go fortsatte på deltid og i parallel med andet ikke-relateret arbejde. I 2008 begyndte Ken Thompsom at arbejde på en kompiler som de kunne bruge til at undersøge ideerne. Kompileren udgav C kode. Midt på året havde projektet udviklet sig til et fuldtidsprojekt, og de var kommet så langt at de kunne forsøge sig på en produktionskompiler. I maj 2008 begyndte Ian Taylor, uafhængigt af projektet, at udvikle en Go frontend til GCC kompileren via det draft de havde lavet til Go specifikationen. Russ Cox sluttede sig til projektet i slutningen af 2008 og hjalp til med flytte sproget og bibliotekkerne fra prototype til virkelighed. Efterfølgende tilsluttede mange andre sig projektet og bidrog med både ideer og kode.

Rob Pike fortæller:

Programmeringssproget Go blev udviklet i slutningen af 2007 som et svar på nogle af de problemer vi som udviklere så i infrastrukturen hos Google. Computer landskabet i dag har stort set ingen relation til det miljø i hvilket de sprog der bruges mest, som C++, Java, og Python, blev skabt i. De problemer der er opstået ved introduktionen af fler-kerne processorer, netværkssystemer, massive computer klynger, samt web udviklingsmodellen, blev der arbejdet udenom fremfor at man løste dem. Derudover har størrelsesordnen ændret sig markant. Programmerne på serverne i dag består af millioner af linjer af kode, og de bliver arbejdet på af flere hundrede eller sågar flere tusinde programører, og de bliver opdateret bogstavlig talt hver dag. For at gøre sagen værre så er tiden det tager at bygge disse programmer, selv på store kompilationsklynger, vokset til mange minutter, ja selv flere timer. Go blev designet og udviklet til at håndterer disse miljø mere produktivt.

Go blev altså født ud af en slags frustration over de nuværende programmeringssprog og miljøer. Programmering ansås for at være blevet for vanskeligt og valget af programmeringssprog bar på årsagen. Enten er man tvunget til at vælge effektiv kompilering og effektiv udførelse, eller også lettere udvikling, men meget langsommere implementering. Disse tre ting fandtes ikke i én kombination i nogle af de udbredte programmeringssprog.

Go er et forsøg på at kombinere den lettelse man opnår ved at bruge et dynamisk og fortolket sprog med effektiviseringen af et kompileret sprog. Samtidig forsøger det at være moderne med understøttelse af flere kerne og bedre netværksunderstøttelse.

Go er også designet til at være ultra hurtig til kompilering. Det må ikke tage mere end et par sekunder at bygge store eksekverbar filer på en enkelt computer. En del af dette er opnået ved at udvikle et meget simpelt, men samtidig meget udtrykfyldt sprog. Ligeledes automatisk hukommelseshåndtering, effektiv udførelse af programmer der kører samtidigt, effektiv håndtering af afhængigheder.

Ingen af de nuværende sprog havde denne sammensætning, hvorfor de følte et stort behov for et nyt sprog.

Ken Thompson var den der designede og implementerede det originale UNIX operativ system, og han var også opfinderen bag programmeringssproget B (den direkte forløber til programmeringssproget C). Han har også en af opfinderne til operativsystemet Plan 9 fra Bell Labs.

Robert Pike er bedst kendt for hans arbejde hos Bell Labs, hvor han også var en af medlemmerne på UNIX holdet. Ligeledes var han også med til at designe og udvikle Plan 9 og operativsystemet Inferno. Han har også været med til at udvikle programmeringssproget Limbo.

Pike er sammen med Brian Kernighan forfater til bogen The Practice of Programming og The Unix Programming Environment. Sammen med Ken Thompson er han også udvikleren af UTF-8.

Robert Griesemer er en af udviklerne bag Google’s V8 JavaScript motor. Han har også udviklet på Chuppy, der er en distribueret låsemekanisme til Google’s GFS distribuerede filsystem og en masse andet.

Programmeringssproget Go er således udviklet af nogle af verdens mest erfarne og dygtige mennesker.

Det første jeg lagde mærke til da jeg besluttede mig for at tage et kig på Go var at den fuldstændig mangel på megen af den funktionalitet der normalt forbindes med et “moderne” programmeringssprog, og som flere sprog vedvarende bliver proppet med.

Som et eksempel kan nævnes både C++ og PHP der i de senere år bliver ved med at vokse med mere og mere funktionalitet til f.eks. bedre understøttelse af det objekt orienterede paradigme.

I kontrast til dette er Go nærmest blevet renset for alle disse ting og det er med god grund. Go er ikke alene udviklet hos Google til at gøre det som Google gør bedst, men det er samtidig udviklet af nogle af de gamle mestre i industrien.

Det der gør programmeringssproget Go til en fantastisk designmæssig bedrift er netop det faktum at designerne havde nogle meget negative reaktioner til de programmeringssprog som de betragter som værende akademiske, teoretiske, eller uproduktive.

Nogle mennesker mener at Go ignorerer alle de fundamentale fremskridt indenfor udvikling af programmeringssprog der er opnået indenfor de sidste 40 års. Det betragter Go som et sprog der er låst fast i 70’erne, og som er blevet designet til middelmådige programmører, men intet kunne være fjernere fra sandheden!

Det udviklerne har gjort er ikke at ignorere de sidste 40 års udvikling indenfor programmeringssprog, derimod har de sorteret alt det overflødige og unødvendigt komplekse fra og smidt det i skraldespanden, og har i stedet valgt at fokuserer på at opnå meget af den samme funktionalitet, blot på en meget mere simpel og effektiv måde, og det mener jeg personligt at de har haft stor succes med.

Hver eneste funktionalitet i Go er blevet udtænkt meget nøje, og dernæst implementeret på den mest effektive måde.

I Go er der ikke noget rent objekt orienteret, ikke noget rent funktionelt, ingen klasser, ingen “inheritance” (hvor klasser arver fra andre klasser), og en masse andet funktionalitet eksisterer ikke.

Men det betyder ikke at Go ikke understøtter objekt orienteret programmering eller funktionel programmering, det gør Go, det bliver bare implementeret på en anderledes og utrolig simpel måde.

Resultatet af Go’s design er at man kan opnår meget af den samme funktionalitet kendt fra programmeringssprog som f.eks. Java og C++, men på nogle meget mere simple måder.

Man kan sige at i Go er der meget mindre at holde øje med, men ikke mindre man kan opnå.

Resultatet er nogle meget effektive programmer der er meget lettere at vedligeholde.

Hvis du vil vide mere om udviklingen af Go kan artiklen Go at Google: Language Design in the Service of Software Engineering, der er en forkortet tale som Rob Pike gav på en konference i Tucson, Arizona i 2012, anbefales. Ligeledes følgende videoer på YouTube:

Fakta om Go

Statisk typet sprog

Go er et statisk typet programmeringssprog, hvilket vil sige at kompileren tjekker variablernes typer under kompilering. Dette betyder i praksis at du som programmør skal definere hvilken type værdi en given variabel skal indeholde, altså om det er f.eks. et tal eller en tekststreng. Det er ikke selve værdien der tjekkes, men derimod kun typen.

Et statisk typet sprog har den store fordel at type fejl opdages før programmet køres. Hvis jeg f.eks. har defineret en variabel til at være en tekststreng, men er kommet til at indtaste et tal som dens værdi, så vil kompileren give mig en fejl ved kompilering da variablens indhold ikke passer til den type jeg oprindeligt definerede variablen med. Jeg bliver dermed forhindret i at kompilere programmet, og skal rette fejlen før jeg kan fortsætte.

Dette er modsat et dynamisk typet programmeringssprog hvor der enten ikke eksisterer noget tjek af variabel typen, eller hvor dette tjek først sker under kørsel (dvs. run-time). Som programmør skal du ikke tænke på typer i et dynamisk typet programmeringssprog, og du kan blot give variablerne de værdier du ønsker. Dette gælder f.eks. programmeringssprog som PHP, Python, og Ruby.

Da der ikke er noget tjek af variabel typen før evt. under kørsel, kan du risikerer at lede længe efter fejl fordi du er kommet til at give en variabel en forkert værdi. Hvis det dynamiske programmeringssprog samtidig tillader at du ændre variabel typen blot ved at ændre indholdet, som i f.eks. PHP eller Python, så bliver fejlen heller ikke opdaget under kørsel.

Vi kan lave et lille program der demonstrerer dette fint i Go.

package main

import "fmt"

func main() {

    // Først deklareres variablen til at være af typen "string".
    // Dette betyder at variablen skal indeholde en tekst streng.
    var foo string
    // Dernæst gives variablen en værdi der passer til typen.
    foo = "Hello world!"

    // Her deklareres variablen også til at være af typen "string".
    var bar string
    // Men da vi giver variablen et indhold af en anden type,
    // får vi en fejl af kompileren når vi forsøger at kompilere programmet.
    bar = 10

    fmt.Println(foo)
    fmt.Println(bar)
}

Go kompileren giver os følgende fejlbesked:

cannot use 10 (type int) as type string in assignment

I PHP og Python, der begge er dynamiske programmeringssprog, kan ovenstående fejl ikke opdages. Vi behøver ikke definere nogen type, og hvis vi ændre typen bliver dette ikke registreret som en fejl.

Et dynamisk programmeringssprog giver derfor programmøren den frihed at han ikke skal bekymre sig om variabel typer, men tilgengæld er det meget lettere for programmøren at komme til at lave fejl i koden.

Følgende program virker fint i Python:

# Vi definerer variablen foo til at være af typen "string" ved
# at give den en tekst streng som indhold.
foo = "Hello world"
print foo

# Her ændrer vi typen til int uden at dette er et problem.
foo = 10
print foo

Og her er det samme eksempel i PHP:

<?php
// Vi definerer variablen foo til at være af typen "string" ved
// at give den en tekst streng som indhold.
$foo = "Hello world";
print $foo;

// Her ændrer vi typen til int uden at dette er et problem.
$foo = 10;
print $foo;

Statisk typet programmeringssprog er derfor mere sikre at programmere i, og det er typisk svære at lave den slags fejl i koden.

Om programmering og paradigmer

UNDER UDVIKLING

Installation

Go tilbydes til en lang række platforme, hvoraf de mest brugte platforme er repræsenteret i disse installationsinstruktioner,

Yderligere detaljer og instruktioner for andre platforme forefindes på golang.org. Disse instruktioner anvender primært platformens pakkesystemer til installation, hvorfor det næppe er allerseneste udgivne Go-udgave, der installeres.
Hvis der er behov for en nyere udgave af Go, kan instruktionerne på golang.org anvendes.

Mac OS X

TBD.

Linux

Go er generelt godt understøttet i diverse Linux-distributioners pakkesystemer. De primære distributioner er dækket her.

Før installation

TBD.

Ubuntu

Debian-baserede distributioner såsom Ubuntu understøtter Go i Apt-pakkesystemet, og kan derfor let installeres således,

sudo apt-get update
sudo apt-get install -y golang

Arch

Installer Go i Arch Linux således,

# pacman -S go

Fedora

Installer Go i Fedora således,

sudo dnf install golang

Dette vil installere Go og dets afhængigheder.

Efter installation

Start en ny shell og bekræft følgende,

Dette kan gøres på følgende vis,

# ls -l $(go env GOPATH)
drwxrwxr-x 2 user group 4096 Dec 26 16:46 bin
drwxrwxr-x 3 user group 4096 Mar 25  2017 pkg
drwxrwxr-x 6 user group 4096 Jan 21 08:50 src

# ls -l $(go env GOROOT)
total 12                       
drwxr-xr-x 2 root root 4096 Apr  6  2017 bin
lrwxrwxrwx 1 root root   35 Dec 19  2016 doc -> ../../share/doc/golang-1.7-doc/html
lrwxrwxrwx 1 root root   45 Apr  5  2017 favicon.ico.gz -> ../../share/doc/golang-1.7-doc/favicon.ico.gz
drwxr-xr-x 5 root root 4096 Apr  5  2017 pkg                                                
lrwxrwxrwx 1 root root   22 Dec 19  2016 src -> ../../share/go-1.7/src
lrwxrwxrwx 1 root root   23 Dec 19  2016 test -> ../../share/go-1.7/test
-rw-r--r-- 1 root root    7 Dec  1  2016 VERSION   

Note: Output varierer i lettere grad mellem distributioner og Go-versioner.

Windows

TBD.

Afprøvning

Installationen kan afprøves ved at oversætte følgende minimale Go-program,

package main

import "fmt"

func main() {
    fmt.Printf("Hej fra Go\n")
}

Kopier og indsæt ovenstående kildekode i filen $GOPATH/src/hej.go. Oversæt derefter kildekoden og afvikl programmet således,

# cd $GOPATH/src
# go build .
# ./hej
Hej fra Go

Hvis der vises fejlbeskeder eller linjen Hej fra Go ikke vises, er Go-installationen fejlramt.

Pakker

I Go bliver programmer organiseret i en mappestruktur kaldet for “arbejdsrum” (workspace). Et arbejdsrum er blot et bibliotek der indeholder en eller flere af dine Go applikationer. Et arbejdsrum indeholder tre undermapper i roden.

Følgende er et eksempel på et arbejdsrum kaldet “GoArbejdsMappe” i din hjemmemappe.

GoArbejdsMappe/
  bin/
  pkg/
  src/

Go programmer konstrueres ved at linke “pakker” sammen. En pakke kan være sammensat af en eller flere filer med kildekode der sammen deklarerer konstanter, variabler, typer og funktioner som hører sammen i pakken, og som alle er tilgængelige for filer i den samme pakke. Disse elementer kan så “eksporteres” så de også kan benyttes i andre pakker.

I en og samme pakke kan der ikke eksistere to identiske deklarationer. Dvs. der kan f.eks. ikke eksistere to funktioner med samme navn i en og samme pakke.

Pakker i Go minder om det der hedder “namespaces” i andre programmeringssprog.

Før du kan udvikle et Go program skal du specificere et arbejdsrum. Dette gør du med GOPATH miljø variablen.

$ export GOPATH=/$HOME/GoArbejdsMappe

En god måde at organisere et Go program på er ved at bruge mappenavne som pakkenavne.

GoArbejdsMappe/
  src/
    foo/
      bar/
        a.go
        b.go
        c.go
      main.go

I GoArbejdsMappe under src har vi en pakke kaldet foo. foo indeholder en main.go, der er selve hovedfilen som Go skal bruge. Derudover indeholder foo en lokal pakke kaldet bar.

Et pakkenavn specificeres via nøgleordet package. main.go skal altid tilhøre main pakken, og det kan ikke laves om.

Go’s standard bibliotek kommer med et hav af rigtigt gode pakker der alle kan bruges til at opbygge forskellige programmer. F.eks. indeholder pakken database/sql et interface til at arbejde med SQL databaser, mens pakken net/http giver HTTP klient og server implementeringer.

Pakkerne der følger med i Go’s standard bibliotek kan ses på: https://golang.org/pkg/

Go har indbygget funktionalitet til effektiv benyttelse af eksterne pakker uanset om det drejer sig om pakker fra Go’s eget standard bibliotek, eller om det drejer sig om tredjeparts pakker på f.eks. Github.

Pakker bliver importeret i Go med nøgleordet import.

Hvis vi tager udgangspunkt i ovenstående eksempel ville filerne se sådan ud:

a.go

package bar

import "fmt"

func Blip() {
    fmt.Println("Hej, jeg er Blip!")
}

Pakken fmt er en pakke i Go’s standard bibliotek der implementerer forskellige input og output funktioner som svarer lidt til C’s printf og scanf.

b.go

package bar

import "fmt"

func Blop() {
    fmt.Println("Hej, jeg er Blop!")
}

c.go

package bar

import "fmt"

func Blup() {
    fmt.Println("Hej, jeg er Blup!")
}

main.go

package main

import (
    "fmt"
    "foo/bar"
)

func main() {
    fmt.Println("Hej, jeg er main!")

    bar.Blip()
    bar.Blop()
    bar.Blup()
}

Filerne a.go, b.go og c.go tilhører alle den samme lokale pakke fordi de ligger i den samme undermappe. Navnet på en pakke behøver ikke være det samme som navnet på et bibliotek, men det er praktisk at benytte det samme navn.

I main.go bliver to forskellige pakker importeret. Først bliver pakken fmt fra Go’s standard bibliotek importeret. Dernæst bliver den lokale pakke bar importeret. Vi importerer den lokale pakke bar ved at bruge pakkenavnet foo. Dette gør vi fordi vi har sat vores GOPATH variabel til at pege på $HOME/GoArbejdsMappe, og Go importerer lokale pakker relativt i forhold til GOPATH.

Hvis du prøver at oprette ovenstående filer kan du teste programmet ved at bruge kommandoen:

$ go run GoArbejdsMappe/src/foo/main.go
Hej, jeg er main!
Hej, jeg er Blip!
Hej, jeg er Blop!
Hej, jeg er Blup!

Bemærk! go run virker kun med filer indeholdende Go kildekode, den virker ikke med mapper. go install derimod virker kun med mapper.

go run kompilerer filen og kører programmet, men den gemmer ikke det kompilerede program.

Hvis du gerne vil oprette en eksekverbar fil af dit program, skal du benytte go install. Det er ligegyldigt i hvilken mappe du befinder dig, når du bruger kommandoen go install, du skal blot specificere navnet på den pakke du ønsker at installere.

$ cd $HOME
$ go install foo

Din mappestruktur ser nu sådan ud:

$ tree
.
└── GoArbejdsMappe
    ├── bin
    │   └── foo
    ├── pkg
    │   └── linux_amd64
    │       └── foo
    │           └── bar.a
    └── src
        └── foo
            ├── bar
            │   ├── a.go
            │   ├── b.go
            │   └── c.go
            └── main.go

Go har oprettet mapperne bin og pkg. Og den eksekverbare fil ligger i bin.

./GoArbejdsMappe/bin/foo
Hej, jeg er main!
Hej, jeg er Blip!
Hej, jeg er Blop!
Hej, jeg er Blup!

Bemærk at funktionerne Blip, Blop og Blup alle starter med et stort begyndelsesbogstav. Det er ikke tilfældigt, men Go’s måde at eksportere ting på.

Når en deklaration af en variabel, en konstant, en funktion eller en struct starter med et stort begyndelsesbogstav, så bliver det der er blevet deklareret “synligt” for anden kode udenfor den pakke som deklarationen finder sted i. Man siger at den pågældende ting der blev deklareret er blevet “eksporteret”. Deklarationer der starter med et lille begyndelsesbogstav, bliver ikke eksporterede, og disse ting er kun synlige for kode der tilhører den samme pakke.

Prøv f.eks. at lave Blop om til blop med et lille “b”. Hvis du gør det, vil du ikke længere kunne kalde funktionen inde fra main.go. Dette skyles at blob nu kun kan ses af kode fra samme pakke som den pakke blob tilhører, altså pakken “bar”. Da kode i main.go tilhører pakken “main”, kan den ikke se de funktioner der ikke er blevet eksporterede.

GoArbejdsMappe/src/foo/main.go:11: cannot refer to unexported name bar.blip
GoArbejdsMappe/src/foo/main.go:11: undefined: bar.blip

Denne funktionalitet kaldes for “synlighed” eller “navnerum”, og i programmering af computere bruges synlighed typisk med henblik på at gruppere symboler og identifikatorer omkring en bestemt funktionalitet, og til at undgå navne kollisioner mellem flere identifikatorer, der deler det samme navn.

main.go kan ikke længere se funktionen blip().

Lad os prøve at lave filerne lidt om og så køre go install foo igen.

a.go

package bar

import "fmt"

func Blip() {
    fmt.Println("Hej, jeg er Blip!")
}

// Funktionen baz() er privat, og den kan kun kaldes
// fra a.go eller fra andre filer i samme pakke, som
// f.eks. b.go eller c.go.
func baz() {
    fmt.Println("Hej, jeg er private baz!")
}

b.go

package bar

import "fmt"

func Blop() {
    fmt.Println("Hej, jeg er Blop!")

    // Her kalder vi funktionen baz() fra a.go
    // Det kan vi fordi a.go og b.go er i samme pakke.
    baz()
}

c.go

package bar

import "fmt"

func Blup() {
    fmt.Println("Hej, jeg er Blup!")
}

main.go

package main

import (
    "fmt"
    "foo/bar"
)

func main() {
    fmt.Println("Hej, jeg er main!")

    bar.Blip()
    bar.Blop()
    bar.Blup()
}
$ go install foo
$ ./GoArbejdsMappe/bin/foo
Hej, jeg er main!
Hej, jeg er Blip!
Hej, jeg er Blop!
Hej, jeg er private baz!
Hej, jeg er Blup!

Funktionen baz() og Blop() er i samme pakke, hvilket gør at Blop() kan se baz() selvom den er privat. Hvis vi forsøger at kalde baz() direkte indefra main() får vi en fejl idet main() ikke kan se baz() direkte. Men da main() kan se Blop(), fordi Blop() er blevet eksporteret via det store begyndelsesbogstav, og da Blop() kan se baz(), bliver baz() kaldt.

Ovenstående kan godt virke lidt forvirrende, og det anbefales at du læser How to Write Go Code

Derudover er det en god idé at eksperimentere lidt med ovenstående indtil du har fået overblik over hvordan det virker.

De fleste mennesker føler en trang til at yde modstand imod måden tingene er sat op på i Go, og det er muligt at gøre tingenen på en anden måde, men målet med den faste opsætning i Go er at få Go udviklere til at arbejde struktureret ens, og når man kigger på hvordan tingene fungere i mange andre programmeringssprog, hvor netop disse ting er til konstant debat og diskussion, så har udviklerne bag Go lukket diskussionen og gennemført en ensartet og effektiv måde at arbejde på. Det fungerer rigtig godt, og kræver blot en lille smule tilvending i starten.

Mere om eksterne pakker

UNDER UDVIKLING

Variabler

En variabel er en pladsholder i computerens hukommelse til noget data. Denne data kan f.eks. være en tekststreng som ordet “hallo verden”, eller det kan f.eks. være et tal som tallet 100, men det kan også være andre ting. Hvorvidt en given værdig er tilladt eller ej fastlægges af variablens type. Variablens type fastsættes ved variablens deklaration.

I Go erklæres en variabel med nøgleordet var.

// Deklarer en variabel af navnet "minTekst", og giv den typen "string".
var minTekst string

// Deklarer en variabel af navnet "mitTal", og den den typen "int".
var mitTal int

// Giv variablen "minTekst" værdien "Hallo verden".
minTekst = "Hallo verden"

// Giv variablen "mitTal" værdien "100".
mitTal = 100

Hvis en variable ikke gives en begyndelsesværdi, initialisere Go variablen til en “nul værdi” for den type man definerer.

Nul værdierne er følgende:

Variabler kan også defineres med en begyndelsesværdi.

var minTekst string = "hallo verden"
var mitTal int = 100

Der findes også en forkortet form hvor Go selv finder ud af variablens type ud fra den værdi man giver den.

var minTekst = "hallo verden"
var mitTal = 100

Sidst, men ikke mindst kan man også erstatte nøgleordet var med semikolon foran lighedstegnet.

minTekst := "hallo verden"
mitTal := 100

Ovenstående forkortede udgave, hvor semikolen bruges i stedet for nøgleordet var, kan kun bruges inde i en funktion. Udenfor en funktion skal enhver variabeldeklaration begynde med nøgleordet var, og den forkortede form := er ikke tilgængelig.

Variablers data kan ændres sålænge værdier er af samme type.

// Begyndende værdi.
var mitTal = 100

// Ny værdi.
mitTal = 200

// Duer ikke da mitTal er deklareret automatisk som en int.
mitTal = "Hej verden"

I det sidste tilfælde giver Go følgende fejlbesked:

cannot use "Hej verden" (type string) as type int in assignment

Det er også muligt at gruppere flere variabel deklarationer sammen. Go tillægger værdierne til variablerne i den rækkefølge de står.

var a, b, c int = 10, 20, 30

// Eller med den forkortede udgave.
a, b, c := 10, 20, 30

Med den forkortede udgave kan man sågar blande typerne.

a, b, c := 10, "Hallo", false

Variablen a bliver deklareret til typen int, og får begyndelsesværdien 10. Variablen b bliver deklareret til typen string, og får begyndelsesværdien “Hallo”. Og variablen c bliver deklareret til typen bool, og får værdien “false”.

Pointer

En “pointer” i Go er en særlig variabel der indeholder en hukommelsesadresse på en anden almindelig variabel. Ligesom en almindelig variabel har en type, så har en pointer variabel også en type. Pointer variablens type fastsættes af den variablen type som pointeren “peger” på.

Hvis jeg opretter en variablen af typen “string” med værdien “Hej verden”, så ligger denne værdi et sted i computerens hukommelse. Hvis jeg skal “pege” på denne hukommelsesadresse med en pointer variabel, så skal jeg bruge en pointer af typen “string”.

Sagt med andre ord: En pointers type skal passe sammen med typen af det som den peger på.

Lad os kigge på et konkret eksempel. Opret filen main.go og indtast følgende:

package main

import "fmt"

func main() {

    // Først deklarer vi variablen "a" af typen "string", og giver den værdien "Hej".
    var a string = "Hej"

    // Dernæst deklarer vi variablen "b" af typen "string pointer".
    // Vi bruger * til at indikere at denne variablen er en "pointer".
    var b *string

    // Her giver vi variablen "b" hukommelsesadressen på variablen "a".
    // Vi bruger & til at indikerer at det er hukommelsesadressen vi ønsker,
    // og ikke selve indholdet af "a".
    b = &a

    // Udskriv værdien der ligger i variablen "a".
    fmt.Println(a)

    // Udskriv værdien der ligger i variablen "b".
    fmt.Println(b)

    // Udskriv værdien af det som "b" peger på.
    fmt.Println(*b)
}

Hvis du kører ovenstående kode vil du se noget tilsvarende det her:

$ go run main.go
Hej
0xc8200721b0
Hej

Værdien 0xc8200721b0 er adressen på variablen a på min computer, den vil højst sandsynligt se anderledes på din.

Tegnet * betyder at en variabel bliver deklareret som en pointer, en variabel der peger på noget, og man kan rent praktisk tænke sig at “stjernen” er det man peger med.

Forskellen på fmt.Println(b) og fmt.Println(*b) er at vi i første tilfælde udskriver værdien af selve variablen b, hvilket er hukommelsesadressen på variablen a. I et andet tilfælde vil vi ikke se selve hukommelsesadressen, men derimod vil vi gerne se hvad der egentligt gemmer sig på denne hukommelsesadresse, hvilket er værdien “Hej” som variablen a indholder.

Tegnet & bruges til at få hukommelsesadresser, og vi kan lave et lille program der viser dette isoleret fra emnet om pointere.

package main

import "fmt"

func main() {

    var foo string = "Hej med dig"

    // Udskriv værdien som foo indeholder.
    fmt.Println(foo)

    // Udskriv den hukommelsesadresse på foo.
    fmt.Println(&foo)
}

Hvis du kører ovenstående program får du noget tilsvarende dette resultat:

Hej med dig
0xc82000a320

Lad os lave et andet konkret eksempel.

package main

import "fmt"

func main() {

    var a string = "foo"

    var b *string = &a

    fmt.Println(a)

    *b = "bar"

    fmt.Println(a)
}

Hvad tror du der sker, hvis du prøver at køre ovenstående program?

$ go run main.go
foo
bar

Efter som *b peger på hukommelsesadressen til variablen a, kan vi ændre på indholdet på selve adressen, og det gør vi med:

*b = "bar"

I ovenstående er det ikke variablen b der bliver ændret, men derimod det indhold som den peger på i hukommelsen, hvilket er indholdet af variablen a.

Man kunne undre sig over hvad formålet med pointere er, og hvis man kommer fra en Python, Ruby eller PHP baggrund, så giver de meget lidt mening. Men der findes specifikke situationer hvor en pointer kan være et meget nyttigt redskab.

Go blev oprindeligt designet til systemprogrammering og til at være et alternativ til C++. Dette betyder at Go blev udstyret med pointere sådan at man har mulighed for at “pille” direkte i data via brug af specifikke hukommelsesadresser. Pointere kan også bruges til at få programmer til at køre hurtigere idet man får direkte kontrol over hvor og hvordan data gemmes i hukommelsen. Selvom det er mikroskopiske forskelle, så er der forskel på hastigheden på udlæsning af en lang tekst streng der ligger spredt rundt omkring i hukommelsen, og så på en tekststreng der ligger samme sted, punkt efter punkt. Dette betyder samtidig også at hukommelsen bliver mindre fragmenteret.

Det er dog kun til systemprogrammering og programmering til indlejrede systemer at man har brug for den slags kontrol.

Grundlæggende set skal du ikke bruge pointere fremfor almindelige variabler. Pointere skal du kun bruge hvor du ikke kan bruge noget som helst andet, eller hvor du reelt har brug for at manipulere med indholdet på specifikke hukommelsesadresser.

Når man kender til globale variabler, kan man forledes til at tænke at en pointer og en global variabel lidt er det samme, men det er ikke korrekt. En global variabel er en variabel der kan tilgås alle steder fra. En pointer er en variabel der indeholder en hukommelsesadresse til hvor noget i hukommelsen bor.

Konstanter

En konstant minder om en variabel, det er også en pladsholder i computerens hukommelse til noget data. Forskellen imellem en variabel og en konstant ligger i at en konstant ikke kan få ændret sit indhold, når den først er erklæret.

I Go erklæres en konstant med nøgleordet const

I Go (og mange andre programmeringssprog) er det god kutyme at gøre brug af store bogstaver, når man definerer konstanter. På den måde er de lette at få øje på i koden, og de er dermed også lette at skelne fra variabler.

const MINTEKST string = "Dette er en konstant"

Hvis du forsøger at ændre værdien af en konstant der allerede er sat, får du en fejl.

const MITTAL int = 100

// Dette virker ikke!
MITTAL = 200

Når man forsøger at ændre indholdet af en konstant giver Go følgende fejlbesked:

cannot assign to MITTAL

Hvilket blot betyder, at Go ikke er i stand til at ændre værdien af konstanten MITTAL da den allerede er sat.

Structs

Structs i Go er en type samling af felter. De er brugbare til at gruppere data sammen.

Lad os se på et simpelt eksempel:

package main

import "fmt"

// Vi opretter en struct af type "person", og giver den felterne "navn" og "alder".
type Person struct {
    navn string
    alder int
}

func main() {

    // Med denne syntaks opretter vi en ny struct.
    fmt.Println(Person{"Brian", 20})

    // Du kan også oprette en struct ved at navngive felterne.
    fmt.Println(Person{navn: "Karina", alder: 30})

    // Hvis du udelader et felt får den sin naturlige nul værdi.
    fmt.Println(Person{name: "Martin"})

    // Du tilgår en struck's felter med et punktum.
    s := Person{navn: "Jonas", age: 50}
    fmt.Println(s.name)
}

Specifikation

Reserverede nøgleord

De følgende nøgleord er reserverede, og kan derfor ikke bruges til identifikation.

break        default      func         interface    select
case         defer        go           map          struct
chan         else         goto         package      switch
const        fallthrough  if           range        type
continue     for          import       return       var

Pre-deklarerede identifikatorer

Types:
    bool byte complex64 complex128 error float32 float64
    int int8 int16 int32 int64 rune string
    uint uint8 uint16 uint32 uint64 uintptr

Constants:
    true false iota

Zero value:
    nil

Functions:
    append cap close complex copy delete imag len
    make new panic print println real recover

Resourcer

Den officielle hjemmeside
Dokumentation
Specifikationen
Wiki
FAQ
Blog
Twitter
Slack
Go tour - tutorial
Forum

Gopheracademy

Gopher Academy er en organisation der er oprettet af Go entusiaster for at udsprede programmeringssproget Go igennem undervisning, samfundsbegivenheder, o.a.

Dave Cheney

Dave er en af dem der organiserer gophercon, og han er meget involveret i Go samfundet.

Ben Johnson

Ben har nogle gode artikler om standard biblioteket, strukturering af Go kode, og meget mere.

Golang Weekly

Et nyhedsbrev der hver uge har nogle gode artikler. Du kan finde gamle artikler her: Issues

William Kennedy

William Kennedy’s (forfatter af Go in Action) blog.

GoBridge

Et Go forum drevet af GoBridge - en interesse organisation hvis mission er at udsprede brugen af Go som et redskab til at lære og undervise programmering, og i sidste ende til at styrke underrepræsenterede grupper i teknologi for at øge mangfoldigheden i Go samfundet.

Golang Reddit

Go på Reddit.

Gophercon

GopherCon er verdens største Go begivenhed dedikeret programmeringssproget Go.

Go CPH

Meetup gruppe for folk interesseret i Go i København og omegn.


Oprettet og vedligeholdt af

Unix Sheikh

Programmering i Go er udgivet under licens:
Attribution 4.0 International.