Rust - from Mozilla

Hello World!               

L'inizio non può che essere il solito, così partiamo con un po' di codice:

  Esempio 2.1
1
2
3
4
fn main()
{
  println!("Hello, World");
}

La riga 1 ci presenta la parola chiave fn che introduce una funzione laddove in altri linguaggi trovate fun o func ecc... Rust, come detto nell'introduzione, ama molto la brevità. In ogni programma deve esistere una funzione che si chiama main e costituisce l'netry point del programma. La sua assenza viene segnalata dal compilatore:

error: main function not found

E' importante notare, cosa del resto facilmente verificabile, che Rust è case sensitive, ovvero è sensibille alla differenza tra maiuscole e minuscole, quindi scrivere, ad esempio Main invece di main è diverso.

La riga 2 e la 4 racchiudono una "frase" del programma, in pratica un blocco di istruzioni, delimitato quindi, come in C, Java, C++, C# e altri, da una coppia di parentesi graffe.

La riga 3 cosituisce il corpo del programma. L'istruzione println termina col punto esclamativo in quanto ci troviamo di fronte non ad una semplice funzione bensì ad una macro. E' una cosa che troveremo spesso in Rust che, in questo modo, propone il suo sviluppo di metaprogrammazione. Delle macro riparleremo ancora, per il momento prendete questo concetto così com'è, tenendo presente che una macro, concettualmente, è simile ad un funzione ma restituisce codice, piuttosto che un valore. Il parametro che viene passato alla macro è una stringa che si presenta delimitata da una coppia di doppi apici, come in molti altri linguaggi. L'istruzione, sempre in C-style, termina col ; (punto e virgola). Nel manuale di Rust si fa riferimento al fatto che ci troviamo di fronte ad una espressione piuttosto che ad un semplice comando. Rust è expression-oriented e quindi le istruzioni restituiscono un valore.

Il passo successivo che propongo di solito è quello di utilizzare un programma con un po' di interattività, ad esempio che permetta di gestire un input. Già da questo esempio potremo imparare un po' di cose interessanti e capire che Rust, insomma, è abbastanza tosto.....

  Esempio 2.2
1
2
3
4
5
6
7
8
9
10
11
use std::io;
use std::io::prelude::*;

fn main()
{
    print!( "Inserisci il tuo nome: ");
    io::stdout().flush().ok().expect("");
    let mut input = String::new();
    io::stdin().read_line(&mut input);
    println!("Ciao {}", input.trim());
}

Le prime due righe caricano le necessarie librerie, per ora chiamiamole così, necessarie per gestire input e output.
La riga 4 la conoscete.
La riga 6 dovrebbe esservi counque famigliare la differenza tra print! e println! è che la seconda istruzione mette un "a capo" dopo aver stampato a video la stringa mentre la prima a capo non ci va. Un'altra differenza tra le due istruzioni, questa un po' più insidiosa, difatti la macro print! viene bufferizzata e non è stampata fino a che non si introduce un "a capo" cosa che invece con println! è automatica. Questo rende necessaria l'istruzione alla riga 7 che forza lo svuotamento del buffer di output. Lo so, non è molto bello e nemmeno l'istruzione stessa è particolarmente user-friendly ma, se non cambia nulla nel linguaggio, ce la teniamo così.
La riga 8 è particolarmente importante. La keyword let serve per dichiarare una variabile nel programma. In realtà la definizione "variabile" è impropria in quanto utilizzando let da solo la nuova entità introdotta è immutabile. Ovvero la scrittura:

let x = 0;

equivale in realtà a creare una costante avente valore 0 che non può essere modificato. Il tentativo di attribuire un diverso valore darebbe origine ad un errore chiaramente indicato dal compilatore:

error: re-assignment of immutable variable `x`

per inciso notiamo che, come in altri linguaggi, il segno = è usato per attribuire un valore ad un elemento del programma. Affichè sia possibile sciogliere questo vincolo è necessario aggiungere, come alla riga 8, un'altra parola riservata, mut, ovvero mutable. Così è ammissibile scrivere:

let mut x = 0;
x = 1;

Ricordatevi bene di questi semplici ma fondamentale concetto.
Sempre alla riga 8 troviamo l'istanziazione di una nuova stringa vuota, ovviamente vedremo più avanti cosa questo significhi.
La riga 9 ci presenta la lettura dallo standard input; il risultato viene riversato nella variabile di nome input, quella dichiarata alla riga precedente.
La riga 10 stampa il quanto letto, la virgola serve per concatenare due stringhe, quella tra i doppi apici e input (che, come visto alla riga 8, è una stringa). In coda alla stringa di output trovate "trim" che ha lo scopo di eliminare eventuali spazi bianchi prima e dopo la stringa che viene stampata. Non è strettamente necessaria ma ne approffitto per mostrare qualche istruzione in più. La stessa riga 10 ci mostra come interpolare una variabile in una stringa, tramite quella coppia di parentesi graffe posta dopo il "ciao".

In Rust il processo di assegnazione di valori a variabili è un po' più completo che in altri linguaggi; la guida ufficiale del linguaggio ci informa che la parte sinistra di una assegnazione è in realtà un "pattern" e questo fatto ci permette di scrivere cose come:

let(x, y) = (1,2);

assegnando quindi ad x il valore 1 e ad y il valore 2.

  Esempio 2.3
1
2
3
4
5
6
fn main()
{
  let (x,y) = (1,2);
  println!("{}",x);
  println!("{}",y);
}

Inoltre, per quanto l'inferenza di tipo faccia egregiamente il suo lavoro, potete anche indicare vuoi l'appartenenza di una variabile ad un certo tipo. La sintassi è:

let [mut] nome-variabile : tipo = valore iniziale

Il mut è opzionale.
Esempio:

let x: i32 = 5

stabilisce che x è un Int32.

Vediamo ora come interfacciarci con i parametri a riga di comando vedendo subito un esempio:

  Esempio 2.4
1
2
3
4
5
6
use std::env;
fn main()
{
  let args: Vec<String> = env::args().collect();
  println!("{:?}", args);
}

La riga 1 presenta l'istruzione use che ci mette a disposizione il contenuto del trait env. E' un po' presto per trattare dei trait ma se venite da Java o C# essi sono molto simili, concettualmente alle interfacce. ovviamente ne parleremo diffusamente a suo tempo. Anche il contenuto della riga 4 è un po' criptico, per ora prendetelo così com'è. L'output del programma comprende i parametri eventiualmente inseriti dopo averlo richiamato ed il nome del programma stesso. Ad esempio:

D:\rustprogs>r0062 pippo
["r0062", "pippo"]

Dove, ovviamente r0062 è il nome che ho dato al programma. A video c'è un array, vero e proprio con tanto di delimitatori. In realtà, spesso, è bene evitare di trascinarsi dietro il nome del programma. Per fare così ricorrere a qualcosa come nel seguente esempio:

  Esempio 2.5
1
2
3
4
5
6
7
use std::env;

fn main() {
  for arg in env::args().skip(1) {
  println!("Argomenti a riga di comando: {}", arg);
  }
}