W języku Go, struktury, zwane także struct, stanowią jeden z fundamentalnych elementów pozwalających na kreowanie własnych, złożonych typów danych.
W niniejszym artykule zagłębimy się w świat struktur, prezentując ich istotę, sposób implementacji i zastosowania w programowaniu w Go, a wszystko to poparte praktycznymi przykładami.
Zaczynajmy!
Wprowadzenie
Struktura to nic innego jak zbiór powiązanych ze sobą danych, gdzie każde pole może być określonego typu. Struktury umożliwiają tworzenie niestandardowych, złożonych rekordów poprzez grupowanie różnych elementów. W skład struktury mogą wchodzić zarówno typy wbudowane, jak i typy definiowane przez użytkownika (przy czym sama struktura jest typem definiowanym przez użytkownika).
Struktury w Go charakteryzują się zmiennością, co oznacza, że ich zawartość może być modyfikowana w trakcie działania programu.
Struktury znacząco podnoszą jakość kodu poprzez możliwość tworzenia i przekazywania złożonych zbiorów danych pomiędzy różnymi modułami. Zamiast przekazywać do funkcji szereg pojedynczych parametrów, możemy zgrupować je w strukturę i operować na niej jako na pojedynczym obiekcie.
Struktury deklaruje się, używając słów kluczowych type i struct. Ich składnię przypomina nieco klasy w Javie, zbiór pól ujęty jest w nawiasy klamrowe. Każde pole ma przypisany konkretny typ i nazwę. Szczegóły implementacyjne omówimy w dalszej części artykułu.
Dla osób mających doświadczenie z programowaniem obiektowym (OOP), struktura może przypominać klasę, z tą różnicą, że nie obsługuje dziedziczenia.
Definiowanie Struktur
Po zapoznaniu się z definicją i zastosowaniem struktur, przyszedł czas na naukę ich deklarowania. Podstawowy schemat struktury prezentuje się następująco:
type nazwa_struktury struct { pole1 typ_danych_pola1 pole2 typ_danych_pola2 }
W powyższym przykładzie, słowa kluczowe „type” i „struct” są elementami składni języka Go. Wewnątrz struktury definiujemy pola wraz z ich typami danych.
Spójrzmy na konkretny przykład:
package main import ( "fmt" ) type Użytkownik struct { imie string wiek int saldoBankowe float32 } func main() { var uzytkownik Użytkownik fmt.Println(uzytkownik) }
W tym przykładzie, tworzymy strukturę o nazwie „Użytkownik”, która składa się z trzech pól: imienia (string), wieku (int) i salda bankowego (float32). W funkcji main(), deklarujemy zmienną „uzytkownik” typu „Użytkownik” i wyświetlamy jej zawartość. Ponieważ struktura nie została jeszcze zainicjalizowana, otrzymujemy tzw. wartość zerową, która jest domyślną wartością dla każdego z pól.
{ 0 0}
Inicjalizacja Struktur
W poprzednim rozdziale nauczyliśmy się deklarować struktury, teraz zajmiemy się przypisywaniem im konkretnych wartości. Poniższy kod ilustruje, jak tego dokonać:
package main import ( "fmt" ) type Użytkownik struct { imie string wiek int saldoBankowe float32 } func main() { // Inicjalizacja z nazwami pól uzytkownik1 := Użytkownik{ imie: "Jan", wiek: 30, saldoBankowe: 1500.0, } // Inicjalizacja bez nazw pól uzytkownik2 := Użytkownik{"Anna", 25, 2500.0} fmt.Println(uzytkownik1) fmt.Println(uzytkownik2) }
Powyższy kod przedstawia dwa sposoby inicjalizacji struktury: z wykorzystaniem nazw pól oraz bez nich. Wynik działania programu będzie następujący:
{Jan 30 1500} {Anna 25 2500}
W przypadku inicjalizacji z pominięciem niektórych pól, przyjmują one domyślną wartość zerową:
uzytkownik1 := Użytkownik{ imie: "Jan", wiek: 30, } // Wynik - {Jan 30 0.0 }
Istnieje również inna metoda tworzenia struktur za pomocą słowa kluczowego `new`. Szczegóły poznamy w następnej sekcji.
Dostęp do pól struktury
Skoro potrafimy już tworzyć i inicjalizować struktury, sprawdźmy, jak uzyskać dostęp do ich pól. W Go wykorzystuje się do tego operator kropki. Na przykładzie wcześniejszego kodu, spróbujmy wyświetlić imię i wiek użytkownika:
package main import ( "fmt" ) type Użytkownik struct { imie string wiek int saldoBankowe float32 } func main() { // Inicjalizacja z nazwami pól uzytkownik := Użytkownik{ imie: "Jan", wiek: 30, saldoBankowe: 1500.0, } fmt.Println(uzytkownik.imie) fmt.Println(uzytkownik.wiek) fmt.Println(uzytkownik.saldoBankowe) }
Używając składni `nazwa_struktury.nazwa_pola`, uzyskujemy dostęp do konkretnych pól struktury. Wynik działania powyższego kodu to:
Jan 30 1500
Jak wspomniano wcześniej, struktury możemy tworzyć również przy pomocy słowa kluczowego `new`. Zobaczmy jak to działa:
uzytkownik := new(Użytkownik) uzytkownik.imie = "Jan" uzytkownik.wiek = 30 uzytkownik.saldoBankowe = 1500.0 fmt.Println(uzytkownik) // Wynik - &{Jan 30 1500}
Słowo kluczowe `new` zwraca wskaźnik do nowo utworzonej struktury. W Go nie ma potrzeby jawnego wyłuskiwania wskaźnika, ale użycie `fmt.Println(*uzytkownik)` dałoby identyczny rezultat.
Zagnieżdżone Struktury
W Go, struktury mogą zawierać w sobie inne struktury. Oznacza to, że możemy tworzyć struktury zagnieżdżone.
package main import ( "fmt" ) type Użytkownik struct { imie string wiek int saldoBankowe float32 detaleRoli DetaleRoli } type DetaleRoli struct { stanowisko string zespół string } func main() { detaleRoliDlaJana := DetaleRoli{ stanowisko: "Programista", zespół: "IT", } uzytkownik := Użytkownik{ imie: "Jan", wiek: 30, saldoBankowe: 1500.0, detaleRoli: detaleRoliDlaJana, } fmt.Println(uzytkownik) }
W powyższym przykładzie, struktura „DetaleRoli” jest częścią struktury „Użytkownik”. Wyjście programu będzie następujące:
{Jan 30 1500 {Programista IT}}
Dostęp do zagnieżdżonych pól uzyskujemy, korzystając z tego samego operatora kropki: `uzytkownik.detaleRoli.stanowisko`.
Porównywanie Struktur
Dwie struktury są sobie równe, gdy wszystkie odpowiadające im pola są równe (zarówno wbudowane, jak i zdefiniowane przez użytkownika). Nie wszystkie typy danych są jednak porównywalne. (na przykład mapa nie jest porównywalna). Sprawdźmy to na przykładzie.
package main import ( "fmt" ) type Użytkownik struct { imie string wiek int saldoBankowe float32 } func main() { uzytkownik1 := Użytkownik{ imie: "Jan", wiek: 30, saldoBankowe: 1500.0, } uzytkownik2 := Użytkownik{ imie: "Jan", wiek: 30, saldoBankowe: 1500.0, } uzytkownik3 := Użytkownik{ imie: "Anna", wiek: 25, saldoBankowe: 2500.0, } if uzytkownik1 == uzytkownik2 { fmt.Println("uzytkownik1 i uzytkownik2 są równe") } else { fmt.Println("uzytkownik1 i uzytkownik2 nie są równe") } if uzytkownik1 == uzytkownik3 { fmt.Println("uzytkownik1 i uzytkownik3 są równe") } else { fmt.Println("uzytkownik1 i uzytkownik3 nie są równe") } }
Puste i zerowe wartości struktur są sobie równe. Kolejność pól nie ma znaczenia, liczy się wyłącznie zgodność wartości poszczególnych pól. Wynik powyższego kodu będzie następujący:
uzytkownik1 i uzytkownik2 są równe uzytkownik1 i uzytkownik3 nie są równe
Podsumowanie
Wspaniale!
Teraz posiadasz wiedzę niezbędną do korzystania ze struktur w języku Go. Przeanalizowaliśmy podstawowe zagadnienia, takie jak deklarowanie, inicjalizowanie i dostęp do pól struktur. Dowiedzieliśmy się również, jak porównywać struktury oraz jak zaimplementować struktury zagnieżdżone. Poniżej kilka linków do dalszej nauki:
W temacie struktur jest jeszcze wiele do odkrycia, jednak to doskonały punkt wyjścia. Mam nadzieję, że ten artykuł wzbogacił Twoją wiedzę!
Eksploruj i ucz się dalej!
newsblog.pl