Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Le funzioni di Rust

Analizziamo le funzioni in Rust, impariamo a crearne una e scopriamo il ruolo di argomenti e valori di ritorno
Analizziamo le funzioni in Rust, impariamo a crearne una e scopriamo il ruolo di argomenti e valori di ritorno
Link copiato negli appunti

I gangli operativi di un programma Rust sono le funzioni costituite da un blocco di istruzioni delimitato da parentesi graffe e associato ad un nome simbolico introdotto, in questo linguaggio, dalla parola chiave fn.

Sin dalle prime righe di codice che scriviamo in Rust abbiamo a che fare con le funzioni. Sappiamo infatti che l'esecuzione di un intero programma Rust inizia da un singolo entry point, rappresentato dalla funzione main:

fn main(){
    // codice da eseguire
}

Come vediamo, main è il nome di una funzione il cui corpo racchiude il codice necessario all'avvio del programma. La struttura vede anche la presenza di una coppia di parentesi tonde che, in generale, sono utilizzate per specificare gli argomenti che saranno passati alla funzione al momento della sua invocazione.

La nostra prima funzione

Al di là del main, scriveremo tantissime funzioni e la nostra bravura come programmatori consisterà proprio nel saper organizzare la logica del programma articolandola in funzioni e nel modo in cui esse verranno attivate. Impariamo quindi a scrivere questi costrutti con un esempio. Supponiamo di voler fare in modo che il nostro hello world, non si occupi di stampare il messaggio all'interno del main ma che, per farlo, invochi una funzione ulteriore:

fn saluta(){
    println!("Ciao mondo! (stampato da una funzione)");
}
fn main() {
    println!("Avvio del programma...");
    saluta();
    println!("...fine del programma");
}

Abbiamo definito una funzione esterna al main denominata saluta che include il println che si occuperà della stampa del messaggio. Tale funzione sarà richiamata all'interno del main mediante l'istruzione saluta().

Questa è la scrittura della nostra prima funzione, diversa dal main, che espone il tipico flusso di lavoro che affronteremo con questi costrutti: una funzione viene definita, viene invocata, svolge le operazioni per cui è programmata dopodiché restituisce il controllo al chiamante.

Serve però saper integrare questo assetto con altri dettagli, in primis l'uso di argomenti e valori di ritorno.

Argomenti e valori di ritorno

Le funzioni costituiscono dei "blocchi" operativi che spesso hanno bisogno di lavorare su valori immessi in input e restituiscono dei risultati. La funzione saluta dell'esempio precedente non necessitava dei primi né restituiva i secondi ma quando una funzione svolge delle elaborazioni deve poter specificare entrambi.

Supponiamo che ci serva una funzione in grado di ricevere due numeri in ingresso e restituirne la somma. In pratica una funzione che riceva come argomenti di lavoro due interi, diciamo di tipo i32, e prometta di restituire come risultato un valore dello stesso tipo. Ecco il codice che potremmo utilizzare:

fn somma(op1:i32, op2:i32) -> i32{
    return op1+op2;
}

La struttura ricalca quello che già sappiamo delle funzioni ma fanno il loro ingresso tre elementi che permettono di gestire il flusso dei dati:

  • tra le parentesi tonde, appaiono gli argomenti che modelleranno quantità e tipologia dei dati da immettere in input al momento della chiamata. In questo caso definiamo op1:i32, op2:i32, due variabili di nome rispettivamente op1 e op2 che riceveranno due numeri di tipo i32;
  • l'operatore freccia (->) introduce il tipo di dato che sarà restituito come risultato;
  • la parola chiave return specifica fisicamente quale valore sarà restituito come risultato.

Provando il codice poniamo nel main una chiamata che effettivamente attiverà la funzione:

fn somma(op1:i32, op2:i32) -> i32{
    return op1+op2;
}
fn main() {
     println!("Somma tra due numeri: {}", somma(10, 12));
}

Con l'espressione somma(10,12) chiamiamo effettivamente il codice della funzione passando come argomenti i numeri 10 e 12 ed il risultato, 22, verrà immesso nel testo dell'output:

Somma tra due numeri: 22

Rust permette di omettere la parola chiave return e ciò risulta particolarmente comodo quando, come per la funzione somma dobbiamo semplicemente restituire il risultato di un'espressione:

fn somma(op1:i32, op2:i32) -> i32{
    op1+op2
}
fn main() {
     println!("Somma tra due numeri: {}", somma(10, 12));
}

In questa forma il programma restituirà lo stesso output di prima ma si faccia attenzione che non va usato il punto e virgola alla fine dell'espressione all'interno della funzione.

Ricorsione nelle funzioni di Rust

Anche in Rust potremo utilizzare la ricorsione ovvero la proprietà di una funzione di restituire un risultato costruito invocando più volte sé stessa. Un tipico esempio è quello del calcolo di un fattoriale che equivale a moltiplicare un numero intero per tutti i valori che lo precedono. Questo può essere calcolato, senza cicli, richiamando la medesima funzione più volte, applicata di volta in volta al valore precedente. Nell'esempio che segue, scriviamo la funzione fattoriale e nel main attiviamo un ciclo che mostrerà i fattoriali di tutti i numeri interi fino a 10:

fn fattoriale(valore : i128) -> i128{
    if valore<=1 {
        return 1;
    }
    valore * fattoriale(valore-1)
}
fn main() {
      for i in 2..=10 {
          println!("Fattoriale di {}: {}", i, fattoriale(i));
      }
}

Il risultato che si ottiene è:

Fattoriale di 2: 2
Fattoriale di 3: 6
Fattoriale di 4: 24
Fattoriale di 5: 120
Fattoriale di 6: 720
Fattoriale di 7: 5040
Fattoriale di 8: 40320
Fattoriale di 9: 362880
Fattoriale di 10: 3628800

Tutto ha funzionato e, come possiamo vedere, nella funzione fattoriale abbiamo utilizzato il return nel blocco condizionale ma non a fine funzione.

Ti consigliamo anche