O Googleovom programskom jeziku Go već smo pisali u časopisu VIDI prije nekoliko godina. U tom trenutku smo ga predstavili kao „alternativni“ jezik za programere, dok danas bez problema o njemu možemo pisati kao o „srednjestrujaškom“ rješenju za različite vrste programera. Iako se od 2007. godine kad su ga svijetu predstavili njegovi autori (Robert Griesemer, Rob Pike i Ken Thompson) nikad nije probio među najpopularnije programske jezike na svijetu, Go je uvijek tu negdje pri samom vrhu popularnosti. Odmah iza deset najpopularnijih predstavnika.
Glavni cilj u razvoju programskog jezika Go bio je povećanje produktivnosti programera u razvoju softvera u modernim hardversko-softverskim okruženjima gdje su sve više zastupljena umrežena računala s većim brojem ugrađenih procesora/jezgri. Prva verzija 1.0 dovršena je u trećem mjesecu 2012. godine, i od tada se redovito objavljuju nove verzije jezika. U trenutku pisanja ovog teksta, stiglo se već i do verzije 1.21.
Danas Go koriste neke od najvećih tvrtki u svijetu kao što su (osim samog Googlea) Meta ili Microsoft. Svakako ćemo spomenuti da je i alat Arduino CLI, koji je manje ili više poznat svima koji koriste neku Arduino platformu, napisan upravo u programskom jeziku Go (https://github.com/arduino/arduino-cli). Kad neki programski jezik odaberete za izradu prevoditelja za drugi programski jezik (kakav koristi Arduino), onda to odmah pokazuje najvažnije karakteristike Go jezika. Izuzetno veliku brzinu prevođenja i izvođenja programskog koda.
Osim brzine, jedna od najvažnijih karakteristika je dostupnost za različite operativne sustave (Windows, macOS, više verzija Linuxa…). Izvorni kod napisan na jednoj platformi može se (u pravilu – o tome malo kasnije) bez problema prevesti u izvršni oblik, ili direktno izvesti na drugom operativnom sustavu. Prateće slike uz tekst pokazuju kako izgleda prevođenje i izvođenje alata Arduino CLI na operativnim sustavima Windows 11 i Kali Linux.
Trenutna popularnost programskih jezika prema „TIOBE Indexu“: Go je trenutno na 12. mjestu s tendencijom rasta popularnosti
Zbog njegove popularnosti među programerima, brojne razvojne okoline normalno prepoznaju Go i podržavaju njegovu sintaksu, što znači da tako uvelike olakšavaju pisanje programskog koda. Kod nekih je ta mogućnost dostupna odmah „iz kutije“, a kod nekih se prvo moraju preuzeti dodatni moduli s podrškom za Go. Kako izgleda takva podrška ugrađena u Microsoftov VSC također se može vidjeti na pratećoj slici, ili to možete provjeriti sami.
Naravno, za pisanje programskog koda možete koristiti bilo kakav tekstualno orijentirani editor, ali korištenje razvojne okoline koja ga prepoznaje donosi brojne prednosti u pogledu brzine pisanja (predlaganje mogućih dovršetaka naredbi) i formatiranja koda. Baš u vezi s formatiranjem koda postoje detalji (o njima malo kasnije), koje će dio programera naviklih na druge programske jezike sigurno mrziti (bar u početku).
Go je moguće učiti i isprobavati i bez razvojne okoline, ako vam je na raspolaganju internetska veza i neki preglednik. A to je sigurno točno, osim ako ne živite u nekoj pećini. Dovoljno je da preglednik usmjerite na svojevrsno igralište za Go, to jest, na adresu https://play.golang.org/. Sam programski jezik Go dostupan je za besplatno preuzimanje za različite operativne sustave na adresi https://golang.org/.
Posebnosti programskog jezika
U svojoj osnovi sintaksa programskog jezika Go je u velikoj mjeri slična sintaksi drugih programskih jezika koji su nalik C-u. Postoji i nekoliko bitnih razlika, koje su prije svega usmjerene na to da sintaksa jezika bude čim jednostavnija tijekom pisanja koda, a izvođenje izvršne verzije aplikacije čim brže i djelotvornije.
U razvojnoj okolini koja prepoznaje Go (na primjer VSC) pisanje i formatiranje programskog koda mora biti potpuno u skladu sa svim pravilima definiranim za Go. Da bi uloga formatiranja u pisanju Go koda bilo što jasnija, pogledajmo odmah jedan vrlo jednostavan primjer početka Go funkcije:
func DBCheckConnection() int { dbErr := “**** Error connecting to MySQL server with parameters “ + dbCS retvalue := 0 // … ostatak programskog koda }
Ako na trenutak zanemarimo razlike u pogledu same sintakse u definiranju funkcije između programskog jezika Go i C, u prethodnom primjeru postoje čak dvije pogreške zbog kojih se kod ne može prevesti u izvršni oblik. Obje pogreške su posljedica formatiranja izvornog koda, a ne nekakve krivo napisane naredbe ili funkcije.
Prva pogreška je u tome što Go zahtijeva da lijeva vitičasta zagrada (namijenjena za označavanje početka neke od dostupnih programskih struktura) obavezno mora biti u istom redu kao i početak te strukture. Dakle, obavezno to mora biti napisano kao u nastavku i nikako drugačije:
func DBCheckConnection() int {
Nakon prethodnog ispravka bi na prvi pogled sve trebalo biti u redu, ali i dalje nije. Go se preko VSC-a još uvijek buni da za funkciju nedostaje pripadajući komentar. Komentar mora na točno određeni način biti napisan ispred početka funkcije. Minimalni ispravni primjer zato ima sljedeći oblik:
// DBCheckConnection ...
func DBCheckConnection() int {
Komentar obavezno mora početi tim istim nazivom funkcije iza kojeg kao apsolutni minimum moraju biti napisane bar tri točke.
Ako ste u drugim programskim jezicima (fleksibilnijima u pogledu formatiranja izvornog koda) navikli da istu stvar pišete na različite načine, a pogotovo ako ne volite pisati komentare, onda vam se Go u startu baš i neće previše dopasti. Međutim, nakon što prihvatite da neke stvari jednostavno moraju biti napravljene tako kako se podrazumijevaju, kao rezultat ćete dobiti ujednačeni izvorni kod, bez obzira na to radi li na njemu jedan ili više programera.
Striktnim poštivanjem pravila za formatiranje izvornog koda istovremeno je izbačena potreba za pisanjem točke-zareza na kraju naredbe, kao što je uobičajeno kod drugih sličnih programskih jezika. Što uopće nije loše, zar ne?
Također, u različitim kontrolnim strukturama poput if naredbi ili petlji, nije potrebno pisati obične zagrade oko provjere ispravnosti izraza o kojem ovisi izvođenje kontrolne strukture. Na primjer, naredba if u sljedećem primjeru:
func oddOrEven(value string) string {
num, _ := strconv.Atoi(value)
if num % 2 == 0 {
return “even”
} else {
return “odd”
}
}
Pogledajmo još jedan primjer kako Go „tlači“ programere.
func DBCheckConnection() int {
dbErr := “**** Error connecting to MySQL server with parameters “ + dbCS
retvalue := 0
suma := 0
// … ostatak programskog koda
}
U prethodnom primjeru smo deklarirali varijablu suma, jer smo sigurni da će nam trebati kasnije da bismo zbrojili neke vrijednosti. Trenutno taj dio koda još nismo implementirali, nego smo samo deklarirali varijablu. Sa stanovišta jezika Go to je ponovno velika greška, jer smo deklarirali resurs u programu, a nismo ga nikad upotrijebili. To je traćenje resursa koje nije dozvoljeno. Drugim riječima, kad nešto deklariramo, moramo odmah pisati i kod za korištenje deklariranog dijela. Eventualno ga možemo privremeno komentirati. Na prvi pogled opet nepotrebna gnjavaža, ali zato se ne može dogoditi da u programu zaostanu nepotrebno korištene varijable i drugi dijelovi. Što je ponovno dobro sa stanovišta veličine i performansi korištenja dovršenog programa.
Poznate tvrtke koje koriste Go: Osim samog Googlea, na popisu su i Meta, Microsoft i još nekoliko poznatih imena
Vratimo se sada ponovno na liniju programskog koda:
num, _ := strconv.Atoi(value)
Funkcija Atoi koristi se za konverziju podatka između različitih tipova podataka, a kao rezultat izvođenja može vratiti dvije vrijednosti (Go funkcije mogu izravno vraćati više vrijednosti). Prva je sama vrijednost nastala konverzijom, a druga je eventualna pogreška nastala pri postupku konverzije. Na primjer, kod nepravilnog podatka za takvu vrstu konverzije. Prema tome, prethodnu funkciju bismo mogli u punom obliku pisati kao:
num, err := strconv.Atoi(value)
Što ako u preostalom dijelu programa nema potrebe da koristimo vrijednost err, baš kao što je to slučaj u početnom primjeru? Go će se po običaju pobuniti da imamo resurs (varijablu err) koji nigdje ne koristimo, pa prevođenje u izvršni oblik neće biti moguće. Upravo za takve situacije služi zamjenski parametar „_“. Njega možemo postaviti na mjesto vrijednosti koju ne planiramo više koristiti, kako Go ne bi stvarao nepotrebne probleme kod prevođenja.
Budući da smo već više puta spomenuli deklaraciju varijabli, idemo malo detaljnije razjasniti ovo područje gdje je Go ponovno malo drugačiji u odnosu na većinu programskih jezika. Sve će biti mnogo jednostavnije za objašnjavanje na primjeru od nekoliko naredbi:
var suma1 int
suma1 = 2
var suma2 float32 = 3.0
suma3 := 0.0
suma3 = float64(suma1) + float64(suma2)
fmt.Println(suma3)
Go zahtijeva da se kod deklariranja svih varijabli točno navede njihov tip, kao što je to učinjeno u slučaju varijabli suma1 i suma2. U slučaju sume2, u istoj naredbi je riješeno i dodjeljivanje početne vrijednosti. Za sada, sve manje ili više poznato iz drugih sličnih programskih jezika.
Arduino CLI: Također je u potpunosti razvijen u Go jeziku
Međutim, kod varijable suma3 je malo drugačija priča. Kod nje nije naveden tip varijable, ali je istovremeno korišten nešto drugačiji simbol „:=“ umjesto običnog znaka za jednakost. Uz pomoć simbola „dvotočka jednako“ Go u pozadini samostalno odrađuje dio posla, te na temelju prepoznate početne vrijednosti sam deklarira tip varijable. U slučaju vrijednosti 0.0 je to float64. To je ujedno razlog zbog kojeg su u naredbi za zbrajanje potrebne dvije funkcije za konverziju između različitih tipova podataka.
Kao što je to uobičajeno kod modernih programskih jezika, Go podržava složene tipove podataka za čuvanje grupe jednostavnih (polja) ili složenih podataka (strukture). Polja u Go jeziku mogu biti fiksne veličine, promjenjive veličine te kolekcije parova ključ-vrijednost. U originalnoj Go terminologiji se takva polja nazivaju: arrays, slices i maps. Budući da korištenje „običnih“ polja fiksne veličine ima dosta ograničenja u praktičnom korištenju, u Go jeziku se češće koristi druga od spomenutih vrsti polja (slices). Slijedi nekoliko naredbi koje demonstriraju korištenje različitih vrsta polja:
var myArray = [4][2]int{ {1,1}, {2,2}, {3,3}, {4,4}}
mySlice := make([]int, 0, 8)
mySlice = append(mySlice, 1, 2, 3, 4, 5, 6, 7, 8)
mySlice = append(mySlice, 9)
myMap := make(map[string]int)
myMap[“C”] = 1
myMap[“Java”] = 2
myMap [“Go”] = 15
Opet slično drugim modernim programskim jezicima, Go omogućava rukovanje sa složenim i međusobno različitim podacima pomoću struktura podataka. Jednostavni primjer deklaracije i korištenja strukture naveden je u nastavku.
package main
import “fmt”
type person struct {
name string
age int
}
func newPerson(name string) *person {
p := person{name: name}
p.age = 42
return &p
}
func main() {
fmt.Println(person{“Bob”, 20})
fmt.Println(person{name: “Alice”, age: 30})
fmt.Println(person{name: “Fred”})
fmt.Println(&person{name: “Ann”, age: 40})
fmt.Println(newPerson(“Jon”))
s := person{name: “Sean”, age: 50}
fmt.Println(s.name)
sp := &s
fmt.Println(sp.age)
sp.age = 51
fmt.Println(sp.age)
}
Podrška u razvojnim okolinama: Go je dobro podržan u različitim okolinama – na primjer, u Microsoft VSC-u
Primjer programskog koda: Sve je uvijek jednako i „lijepo“ formatirano, pa nema potrebe za posebnim znakom za odvajanjem kraja reda
Prethodni primjer preuzet je s web adrese https://gobyexample.com/, koja predstavlja vrlo dobro mjesto za upoznavanje s programskim jezikom Go preko niza primjera.
Go ne podržava klase izravno, jer su autori smatrali da to nije djelotvoran način rukovanja podacima. Umjesto toga, morat ćete se snaći sa strukturama i dodatnim funkcijama za rukovanje strukturama.
Zato je mjesto gdje se Go vrlo dobro snalazi (uostalom, to je bilo i jedno od polazišta u njegovom stvaranju) istovremeno izvođenje većeg broja niti, tako da one međusobno mogu komunicirati korištenjem kanala. Naravno, ova tematika zahtijeva detaljnije objašnjenje kako bi se shvatile sve mogućnosti, a za to svakako nije mjesto u kraćem uvodnom tekstu u časopisu. Zato ćemo u nastavku samo demonstrirati dio mogućnosti korištenjem odgovarajućeg primjera (ponovno preuzetog s ranije spomenute stranice s Go primjerima).
package main
import “fmt”
func ping(pings chan<- string, msg string) {
pings <- msg
}
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, “passed message”)
pong(pings, pongs)
fmt.Println(<-pongs)
}
Dodatne biblioteke potrebne za razvoj: Uključuju se u posebnoj mod datoteci i automatski preuzimaju po potrebi kod prevođenja
Go podržava brojne dodatne mogućnosti koje se očekuju od modernih programskih jezika, kao što su čitanje i zapisivanje vrijednosti u datoteku, podrška za JSON i XML podatke, preuzimanje parametara iz komandne linije kod pokretanja programa, rukovanje s datotekama i mapama na disku, različite matematičke funkcije, obrada pogreški tijekom izvođenja programa, povezivanje s bazama podataka, i još dosta toga bez čega općenamjenski programski jezik nema nikakve šanse za uspjeh.
Primjer online korištenja programskog jezika: Primjer se izvodi u „igralištu za Go“ (play.golang.org)
Sve do sada nabrojeno ne bi ni izbliza bilo dovoljno kad se Go ne bi mogao povezati i na baze podataka. Za takvo korištenje potrebno je uključiti odgovarajuće biblioteke, kao što to pokazuje sljedeći primjer povezivanja s MySQL bazom podataka (dio jednog složenijeg alata za analizu baza podataka razvijenog od strane autora teksta). Uz promjenu biblioteka moguće je povezivanje i s drugim najpopularnijim bazama podataka.
package main
import (
“database/sql”
“fmt”
“os”
“strconv”
“time”
_ “github.com/go-sql-driver/mysql”
)
…
dbSQL = `SELECT TABLE_NAME, ENGINE, TABLE_ROWS, AVG_ROW_LENGTH, DATA_LENGTH
FROM information_schema.tables WHERE table_schema = ‘` + cliprm.database + `’ and TABLE_TYPE = ‘BASE TABLE’
ORDER BY TABLE_NAME`
if retvalue == 0 {
results, err := dbCN.Query(dbSQL)
if err != nil {
fmt.Println(“error”)
fmt.Println(dbErr + “Tables”)
retvalue = -1
}
if retvalue == 0 {
for results.Next() {
fmt.Print(“.”)
if cliprm.currlevel != “level1” {
f.WriteString(“\n”)
}
var table Level1Size
err = results.Scan(&table.TableName, &table.Engine, &table.Rows, &table.RowSize, &table.DataSize)
if err != nil {
panic(err.Error())
}
istr := strconv.Itoa(i)
line := istr + “. “ + table.TableName + “ (“ + table.Engine + “)\n”
if cliprm.currlevel == “level1” && i == 1 {
f.WriteString(“\n”)
}
f.WriteString(line)
I++
…
Izvođenje programa u Windowsima: Primjer izvođenja programa arduino-cli
Izvođenje programa u Kali Linuxu: Isti program bez ikakvih promjena izvodi se na drugom operativnom sustavu
Nakon što smo napisali programski kod, možemo ga odmah izvesti ili ga prevesti u izvršni oblik za naknadno samostalno izvođenje. Pod pretpostavkom da je program spremljen u datoteku test.go, izravno izvođenje možemo pokrenuti u odgovarajućem prozoru za upis komandi pomoću naredbe:
go run test.go
Za prevođenje programa u izvršni oblik treba koristiti sljedeći oblik:
go build test.go
Kao rezultat izvođenja prethodne naredbe na Windows platformi dobiva se izvršna datoteka test.exe, a nju možete kasnije pokrenuti nezavisno od razvojne okoline, odnosno kao i svaki drugi izvršni program. Slično tome, i na drugim operativnim sustavima dobivaju se izvršne verzije programa. Na primjer, na Linuxu bi takva datoteka dobila naziv test.
Najvažnije novosti u najnovijim verzijama
Tijekom tekuće 2023. godine pojavile su se dvije nove verzije programskog jezika: 1.20 i 1.21. Pogledajmo neke od najvažnijih promjena i novosti.
Naglasak u razvoju Go jezika je od samog početka na njegovoj brzini prevođenja i izvođenja. Tako da nije neočekivano da se u svakoj novoj verziji to pokušava dodatno unaprijediti. Iako prikazano u postocima poboljšanja u odnosu na prethodnu verziju to ne izgleda kao veliko unapređenje, svaki postotak povećanja brzine u odnosu na već veliku početnu brzinu nije uopće zanemariv.
Tako bi verzija 1.20 uz pomoć korištenja PGO (Profile Guided Optimization) tehnike koja provodi dodatnu optimizaciju brzine izvođenja na temelju podataka o korisnikovoj izvršnoj okolini, kao i na temelju drugih optimizacija provedenih u pratećim bibliotekama i alatima, trebala povećati brzinu izvođenja uobičajenih aplikacija za 3 do 4 posto. U verziji 1.21 dodatno unapređivanje korištenja PGO-a trebalo bi povećati brzinu korištenja do 7% u odnosu na prethodne verzije bez PGO.
Što se tiče samih promjena u programskom jeziku, radilo se na pisanju takozvanog generičkog koda pomoću nove ključne riječi comparable. Također je unaprijeđena konverzija između nekih tipova podataka (array i slice), kao i način na koji se točno uspoređuju elementi u njima.
U sam jezik (od 1.21) su dodane tri nove funkcije - min, max i clear - namijenjene provjeri najmanje te najveće dozvoljene vrijednosti za određeni argument, odnosno za brisanje svih elemenata u slice strukturi.
Što se tiče unaprjeđenja u pratećim bibliotekama, ona se odnose na dodatnu podršku u biblioteci za kriptiranje (takozvana Elliptic Curve Diffie-Hellman razmjena ključeva), obradi grešaka, dodatnim mogućnostima u obradi HTTP zahtjeva (http.ResponseController i httputil.ReverseProxy), upravljanju mogućnostima izvođenja komandi operativnog sustava kod njihovog prekida (waitDelay), te dodatnim mogućnostima i optimizacijom brzine sa strukturama array i slice u kombinaciji sa sličnim unapređenjima u samom programskom jeziku.
Dodatni alati koji se isporučuju uz programski jezik u obje verzije također su doživjeli nekoliko unaprjeđenja, od kojih svakako treba spomenuti upravljanje načinom korištenja ranije spomenute PGO mogućnosti, odnosno analizom cjelokupnog koda prilikom prevođenja, a ne samo nekih njegovih jedinica.
Zašto se Go ipak ne probija među najpopularnije programske jezike?
Nakon svih napisanih „hvalospjeva“, logično se postavlja pitanje zašto onda Go nije na samom vrhu popularnosti programskih jezika? Zato što zapravo nije najpogodniji za razvoj svih vrsta aplikacija. Na primjer, na različitim operativnim sustavima treba koristiti posebne dodatne biblioteke da bi se dobilo odgovarajuće grafičko korisničko sučelje (ako se žele pisati programi s korisničkim sučeljem). A to onda sprečava izravno prevođenje programa na drugim sustavima, kao što je to moguće kod programa koji se izvode u komandnoj liniji.
Zbog toga Go vjerojatno neće nikako postati tako popularan kao neki drugi programski jezici pogodni za pisanje svih vrsta aplikacija. Ali će mu zato popularnost za izradu rješenja koja se izvode u komandnoj liniji ili „u pozadini“ na serveru vjerojatno i dalje kontinuirano rasti.