Golang – po co kolejny język?

Dziś chcę napisać nieco o Go (Golang) i jego filozofii. Trochę mnie tu nie było, ale ostatnio pomyślałem, że pisanie o Go jest całkiem ciekawe, bo i sam język jest interesujący. Mam nadzieję, że kogoś nim zainteresuję. Chcę też napisać, po co w ogóle kolejny język powstał. Zapraszam do czytania!

Trochę historii i opisu Golang

Go to kompilowany język programowania, który pojawił się w 2009 roku za sprawą inżynierów Google: Roberta Griesemera, Roba Pike’a oraz Kena Thompsona. Od tamtej pory język się rozwija i zyskuje na popularności. Obecna wersja (na dzień 14. października 2020) to v1.5. Go charakteryzuje się 3 cechami: wydajność, niezawodność i prostota.

Golang jest kompilowany bezpośrednio do kodu maszynowego. Nie ma żadnej platformy uruchomieniowej tak, jak to jest w przypadku Kotlina czy Javy (JVM) albo C# (.NET). Jednocześnie w przeciwieństwie do języków pokroju C/C++, nie musimy troszczyć się o samodzielne zarządzanie pamięcią, bo robi to za nas Garbage Collector.

Język jest dość mocno używany w infrastrukturze. Np Docker czy Kubernetes są napisane w Go. Podobnie Mattermost (taki Open Source’owy Slack), również został napisany w Go. Powszechny jest oczywiście w Google, gdzie język ten powstał.

Nie mniej w Go można spokojnie napisać różne inne aplikacje: konsolowe komendy, webaplikacje, mikroserwisy czy gry. Jednak uważam, że należy i tak dobierać rozwiązanie do problemu, więc niekoniecznie to, że się da, znaczy, że się powinno ;).

suseł-maskotka golanga
Suseł – maskotka języka

Filozofia

Można się zastanawiać, po co kolejny język na rynku. Cóż, zazwyczaj nowe rozwiązania powstają w odpowiedzi na konkretne potrzeby. Tak też było w tym wypadku. Google potrzebowało czegoś szybkiego, wydajnego i prostego. Czegoś, w czym nie będzie mnóstwo pustego kodu. Podobno przeciętny programista tworzy na każde 1000 linijek około 5 błędów. Co to oznacza? Kod, który składa się z miliona linijek potencjalnie zawiera około 5000 błędów. Całe mnóstwo.

Prostota Golang

Jeśli chodzi o prostotę składni, to jest ona do bólu prosta. Pozbyto się wielu elementów, które są znane w typowych obiektowych językach programowania. Nie ma stricte dziedziczenia, nie ma czegoś takiego jak klasa, nie ma klas abstrakcyjnych czy konstruktorów, nie ma wyjątków, a więc nie ma try.. catch’a, nie ma generyków. Co więcej, da się bez tego żyć i jest to całkiem proste i przyjemne.

Wydajność

Golang jest niesamowicie szybki. Zawdzięcza to m.in kompilacji do kodu maszynowego. Ale szybkość pojawia się też w wymiarze samego developmentu. Składnia jest bardzo prosta, a konwencje nakazują tworzyć tak prosto, jak to tylko możliwe, co też niejednokrotnie przenosi się na zadowolenie biznesu. Go ma też świetnie wsparcie dla współbieżności, w przypadku niektórych zadań odpalamy sobie dodatkowe tzw goroutines, co przyśpiesza przetwarzanie dużej ilości danych.

Niezawodność

Jeśli chodzi o niezawodność, to kilka spraw. Po pierwsze jest to obsługa błędów. Go wymusza świadomą obsługę błędów i zapobiega „zapomnieniu złapania wyjątku”. Go już na poziomie kompilacji sprawdza, czy nie mamy zadeklarowanych a nie używanych zmiennych. Zadeklarowane, a nie zainicjowane zmienne mają swoją wartość zerową by default a nie jak to bywa, z randomową wartością.

Przykładowy program

Dla zaprezentowania, jak wygląda kod (dla większości pewnie koszmarnie). Nie mniej idzie się do tego przyzwyczaić.

package main

import "fmt"

func main() {
    fmt.Print("Hello world!");
}

No dobra, Hello World to nie najlepszy przykład. Stwórzmy więc prosty webserwer:

// main.go
package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
)

func main() {
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

    go func() {
        http.HandleFunc("/", handleHome)
        log.Fatal(http.ListenAndServe(":8080", nil))
    }

   <-stop
    os.Exit(0)
}

func handleHome(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello world!")
}

Co tu się dzieje? Ano tworzymy sobie kanał, który będzie służył do otrzymania sygnału zatrzymania od systemu (ctrl + C). Następnie tworzymy goroutyne, w której uruchamiamy serwer. Potem wypisujemy wartość z kanału (tutaj nasza główna goroutyna się zatrzyma czekając na sygnał). Kiedy taki nadejdzie, zamykamy program.