00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Valori di Ritorno delle Funzioni in Go

I valori di ritorno delle funzioni in Go offrono funzionalita uniche rispetto a molti altri linguaggi. La possibilita di restituire piu valori e alla base del modello idiomatico di gestione degli errori di Go. In questa guida esploreremo i ritorni singoli, multipli, con nome e i pattern piu comuni.

Ritorno Singolo

La forma piu semplice: una funzione che restituisce un singolo valore.

package main

import "fmt"

func quadrato(n int) int {
    return n * n
}

func main() {
    risultato := quadrato(7)
    fmt.Println("Il quadrato di 7 e:", risultato) // 49
}

Il tipo di ritorno viene dichiarato dopo la lista dei parametri.

Ritorni Multipli

Go permette di restituire piu valori da una funzione. I tipi dei valori di ritorno vengono racchiusi tra parentesi.

package main

import (
    "fmt"
    "math"
)

func coordinate(angolo float64) (float64, float64) {
    x := math.Cos(angolo)
    y := math.Sin(angolo)
    return x, y
}

func main() {
    x, y := coordinate(math.Pi / 4)
    fmt.Printf("x=%.4f, y=%.4f\n", x, y)
}

I valori vengono restituiti separati da virgola e assegnati a variabili corrispondenti al momento della chiamata.

Valori di Ritorno con Nome

I valori di ritorno possono avere un nome. Queste variabili sono inizializzate al valore zero del loro tipo e possono essere utilizzate nel corpo della funzione.

func divisioneConResto(dividendo, divisore int) (quoziente, resto int) {
    quoziente = dividendo / divisore
    resto = dividendo % divisore
    return // naked return: restituisce quoziente e resto
}

func main() {
    q, r := divisioneConResto(17, 5)
    fmt.Printf("17 / 5 = %d con resto %d\n", q, r) // 3 con resto 2
}

Il return senza argomenti (naked return) restituisce automaticamente le variabili nominate. Questo e consigliato solo in funzioni brevi per mantenere la leggibilita.

Ritorni con Nome: Vantaggi nella Documentazione

I nomi dei valori di ritorno appaiono nella documentazione generata da go doc, rendendo l’API piu comprensibile.

// MinMax restituisce il valore minimo e massimo di una slice di interi.
func MinMax(valori []int) (minimo, massimo int) {
    if len(valori) == 0 {
        return 0, 0
    }
    minimo = valori[0]
    massimo = valori[0]
    for _, v := range valori[1:] {
        if v < minimo {
            minimo = v
        }
        if v > massimo {
            massimo = v
        }
    }
    return
}

Leggendo la firma della funzione, il programmatore capisce subito cosa rappresenta ciascun valore restituito.

Il Pattern Errore come Secondo Valore

Il pattern idiomatico piu importante in Go: le funzioni che possono fallire restituiscono l’errore come ultimo valore di ritorno.

package main

import (
    "fmt"
    "strconv"
)

func convertiInIntero(s string) (int, error) {
    n, err := strconv.Atoi(s)
    if err != nil {
        return 0, fmt.Errorf("conversione fallita per %q: %w", s, err)
    }
    return n, nil
}

func main() {
    // Caso di successo
    valore, err := convertiInIntero("42")
    if err != nil {
        fmt.Println("Errore:", err)
        return
    }
    fmt.Println("Valore:", valore)

    // Caso di errore
    valore2, err := convertiInIntero("abc")
    if err != nil {
        fmt.Println("Errore:", err)
        return
    }
    fmt.Println("Valore:", valore2)
}

Questo pattern sostituisce le eccezioni di altri linguaggi con un meccanismo esplicito e prevedibile.

Gestione degli Errori con If e Short Statement

La combinazione della short statement con il controllo dell’errore e un pattern estremamente frequente.

import "os"

func leggiFile(percorso string) ([]byte, error) {
    if contenuto, err := os.ReadFile(percorso); err != nil {
        return nil, fmt.Errorf("impossibile leggere %s: %w", percorso, err)
    } else {
        return contenuto, nil
    }
}

Oppure, nella forma piu idiomatica con ritorno anticipato:

func leggiFile(percorso string) ([]byte, error) {
    contenuto, err := os.ReadFile(percorso)
    if err != nil {
        return nil, fmt.Errorf("impossibile leggere %s: %w", percorso, err)
    }
    return contenuto, nil
}

Ignorare i Valori di Ritorno con _

Quando un valore di ritorno non e necessario, si usa il blank identifier _.

// Ignora l'errore (da usare con cautela)
contenuto, _ := os.ReadFile("config.txt")

// Ignora il primo valore
_, errore := fmt.Println("test")

// Ignora tutti i valori (raro ma valido)
// fmt.Println restituisce (int, error)
fmt.Println("Questo stampa e basta")

Nota: ignorare gli errori e generalmente sconsigliato nella programmazione Go. E una pratica accettabile solo in contesti dove il fallimento e innocuo.

Funzioni che Restituiscono Solo un Errore

Alcune funzioni eseguono un’azione e restituiscono solo un eventuale errore.

import "os"

func scriviFile(percorso, contenuto string) error {
    err := os.WriteFile(percorso, []byte(contenuto), 0644)
    if err != nil {
        return fmt.Errorf("scrittura fallita: %w", err)
    }
    return nil
}

func main() {
    if err := scriviFile("output.txt", "Ciao mondo"); err != nil {
        fmt.Println("Errore:", err)
        return
    }
    fmt.Println("File scritto con successo")
}

Ritorno Anticipato per Chiarezza

Il pattern del ritorno anticipato migliora la leggibilita evitando nidificazioni eccessive.

func elaboraDati(dati []byte) (string, error) {
    if len(dati) == 0 {
        return "", fmt.Errorf("dati vuoti")
    }

    if dati[0] != '{' {
        return "", fmt.Errorf("formato non valido")
    }

    // Logica principale senza nidificazione
    risultato := string(dati)
    return risultato, nil
}

Conclusione

Il sistema di valori di ritorno multipli di Go e il fondamento della gestione degli errori nel linguaggio. Il pattern (valore, error) e idiomatico e universalmente adottato nella comunita Go. I ritorni con nome migliorano la documentazione e i naked return semplificano funzioni brevi. Padroneggiare questi pattern e essenziale per scrivere codice Go robusto e leggibile.