Sobes.tech
Senior

Šta je suma tipova i kako je implementirati u Go?

sobes.tech АИ

Одговор од АИ

Zbir tipova (ili označena unija, disjunktna unija) je tip podataka koji može da prihvati vrednost jedne od nekoliko unapred definisanih opcija. Za razliku od proizvoda tipova (struktura), gde vrednost sadrži sva polja istovremeno, zbir tipova sadrži samo jednu od mogućih vrednosti.

U Go jeziku, nema direktne podrške za zbir tipova kao u jezicima poput Haskell ili Rust (enum). Zbir tipova može se emulirati na sledeće načine:

  1. Interfejsi i tvrdnja o tipu (Type Assertion):

    Kreirajte interfejs koji će implementirati sve moguće opcije. Vrednost zbira tipova će predstavljati taj interfejs, a za određivanje konkretne opcije koristi se tvrdnja o tipu ili type switch.

    package main
    
    import "fmt"
    
    // Shape - interfejs koji predstavlja zbir tipova
    type Shape interface {
    	Area() float64
    }
    
    // Circle - jedna od opcija
    type Circle struct {
    	Radius float64
    }
    
    func (c Circle) Area() float64 {
    	return 3.14 * c.Radius * c.Radius
    }
    
    // Rectangle - druga opcija
    type Rectangle struct {
    	Width, Height float64
    }
    
    func (r Rectangle) Area() float64 {
    	return r.Width * r.Height
    }
    
    func main() {
    	shapes := []Shape{Circle{Radius: 5}, Rectangle{Width: 3, Height: 4}}
    
    	for _, s := range shapes {
    		// Korišćenje switch-a na tipu za određivanje opcije
    		switch v := s.(type) {
    		case Circle:
    			fmt.Printf("Krug sa poluprečnikom %.2f, površina: %.2f\n", v.Radius, v.Area())
    		case Rectangle:
    			fmt.Printf("Pravougaonik sa dimenzijama %.2f x %.2f, površina: %.2f\n", v.Width, v.Height, v.Area())
    		default:
    			fmt.Println("Nepoznat oblik")
    		}
    	}
    }
    
  2. Strukture sa boolean zastavicama (retko korišćene):

    Struktura sadrži polja za sve moguće opcije i boolean zastavice koje ukazuju koja je aktivna. Ovo je nesigurno i teško za održavanje.

    package main
    
    import "fmt"
    
    type Result struct {
    	Value int  // Polje za uspešan rezultat
    	Err   error // Polje za grešku
    
    	IsValue bool // Zastavica koja ukazuje da je Value aktivno
    	IsErr   bool // Zastavica koja ukazuje da je Err aktivno
    }
    
    func Process(input int) Result {
    	if input > 0 {
    		return Result{Value: input * 2, IsValue: true}
    	}
    	return Result{Err: fmt.Errorf("negativan broj: %d", input), IsErr: true}
    }
    
    func main() {
    	res1 := Process(10)
    	if res1.IsValue {
    		fmt.Printf("Rezultat: %d\n", res1.Value)
    	} else if res1.IsErr {
    		fmt.Printf("Greška: %v\n", res1.Err)
    	}
    
    	res2 := Process(-5)
    	if res2.IsValue {
    		fmt.Printf("Rezultat: %d\n", res2.Value)
    	} else if res2.IsErr {
    		fmt.Printf("Greška: %v\n", res2.Err)
    	}
    }
    
  3. Strukture sa nul vrednošću za neaktivna polja (često korišćene za Optional/Result):

    Struktura sadrži polja za sve moguće opcije, ali samo jedno od njih će biti nenultno. Dobro funkcioniše za pokazivače ili tipove sa jasnom "nultom" vrednošću.

    package main
    
    import "fmt"
    
    // Option - emulacija Some/None iz opcionog tipa
    type Option struct {
    	Value *int // Nenultno ako postoji vrednost
    }
    
    // None kreira nultu vrednost Option
    func None() Option {
    	return Option{}
    }
    
    // Some kreira Option sa vrednošću
    func Some(val int) Option {
    	return Option{Value: &val}
    }
    
    func main() {
    	opt1 := Some(10)
    	if opt1.Value != nil {
    		fmt.Printf("Vrednost: %d\n", *opt1.Value)
    	} else {
    		fmt.Println("Nema vrednosti")
    	}
    
    	opt2 := None()
    	if opt2.Value != nil {
    		fmt.Printf("Vrednost: %d\n", *opt2.Value)
    	} else {
    		fmt.Println("Nema vrednosti")
    	}
    }
    

Najidiomatičniji i najsigurniji način emulacije suma tipova u Go je korišćenje interfejsa i type switch. To garantuje da je prisutna samo jedna od opcija i obezbeđuje tipsku sigurnost pri radu s njom.