Sobes.tech
Senior

Co je to součet typů a jak jej implementovat v Go?

sobes.tech AI

Odpověď od AI

Součet typů (nebo označené sjednocení, disjunktní sjednocení) je datový typ, který může nabývat hodnoty jedné z několika předdefinovaných možností. Na rozdíl od typu produktu (struktur), kde hodnota obsahuje všechna pole současně, součet typů obsahuje pouze jednu z možných hodnot.

V Go není přímá podpora součtu typů jako v jazycích jako Haskell nebo Rust (enum). Součet typů lze napodobit několika způsoby:

  1. Rozhraní a tvrzení typu (Type Assertion):

    Vytvořte rozhraní, které budou implementovat všechny možné varianty. Hodnota součtu typů bude představovat toto rozhraní, a pro určení konkrétní varianty se používá tvrzení typu nebo type switch.

    package main
    
    import "fmt"
    
    // Shape - rozhraní reprezentující součet typů
    type Shape interface {
    	Area() float64
    }
    
    // Circle - jedna z variant
    type Circle struct {
    	Radius float64
    }
    
    func (c Circle) Area() float64 {
    	return 3.14 * c.Radius * c.Radius
    }
    
    // Rectangle - jiná varianta
    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 {
    		// Použití switch na typ pro určení varianty
    		switch v := s.(type) {
    		case Circle:
    			fmt.Printf("Kruh s poloměrem %.2f, plocha: %.2f\n", v.Radius, v.Area())
    		case Rectangle:
    			fmt.Printf("Obdélník s rozměry %.2f x %.2f, plocha: %.2f\n", v.Width, v.Height, v.Area())
    		default:
    			fmt.Println("Neznámý tvar")
    		}
    	}
    }
    
  2. Struktury s booleanovými vlajkami (zřídka používané):

    Struktura obsahuje pole pro všechny možné varianty a booleanové vlajky, které ukazují, která varianta je aktivní. Je to nebezpečné a složité na údržbu.

    package main
    
    import "fmt"
    
    type Result struct {
    	Value int  // Pole pro úspěšný výsledek
    	Err   error // Pole pro chybu
    
    	IsValue bool // Vlajka, která ukazuje, zda je aktivní Value
    	IsErr   bool // Vlajka, která ukazuje, zda je aktivní Err
    }
    
    func Process(input int) Result {
    	if input > 0 {
    		return Result{Value: input * 2, IsValue: true}
    	}
    	return Result{Err: fmt.Errorf("záporné číslo: %d", input), IsErr: true}
    }
    
    func main() {
    	res1 := Process(10)
    	if res1.IsValue {
    		fmt.Printf("Výsledek: %d\n", res1.Value)
    	} else if res1.IsErr {
    		fmt.Printf("Chyba: %v\n", res1.Err)
    	}
    
    	res2 := Process(-5)
    	if res2.IsValue {
    		fmt.Printf("Výsledek: %d\n", res2.Value)
    	} else if res2.IsErr {
    		fmt.Printf("Chyba: %v\n", res2.Err)
    	}
    }
    
  3. Struktury s nulovou hodnotou pro neaktivní pole (často používané pro Optional/Result):

    Struktura obsahuje pole pro všechny možné varianty, ale pouze jedno z nich bude nenulové. Funguje dobře pro ukazatele nebo typy s jasnou "nulovou" hodnotou.

    package main
    
    import "fmt"
    
    // Option - emulace Some/None z volitelného typu
    type Option struct {
    	Value *int // Nenulové, pokud je hodnota přítomna
    }
    
    // None vytváří nulovou hodnotu Option
    func None() Option {
    	return Option{}
    }
    
    // Some vytváří Option s hodnotou
    func Some(val int) Option {
    	return Option{Value: &val}
    }
    
    func main() {
    	opt1 := Some(10)
    	if opt1.Value != nil {
    		fmt.Printf("Hodnota: %d\n", *opt1.Value)
    	} else {
    		fmt.Println("Žádná hodnota")
    	}
    
    	opt2 := None()
    	if opt2.Value != nil {
    		fmt.Printf("Hodnota: %d\n", *opt2.Value)
    	} else {
    		fmt.Println("Žádná hodnota")
    	}
    }
    

Nejidiomatičtější a nejbezpečnější způsob emulace součtu typů v Go je použití rozhraní a type switch. To zaručuje, že je přítomen pouze jeden z variant a poskytuje typovou bezpečnost při práci s ním.