Rust – il linguaggio – 4

COD

Oggi, alle prese con Rust, sono qui: /usr/local/share/doc/rust/html/book/functions.html, continuando da qui.

Funzioni

Ogni programma ha almeno una funzione main.

fn main() {
}

Siccome si racconta di cose già viste tante volte riassumo. Ecco un esempio semplice:

fn main() {
    print_sum(5, 6);
}

fn print_sum(x: i32, y: i32) {
    println!("sum is: {}", x + y);
}

rs4-0

Una nota per me: precedentemente avevo detto che avrei usato sempre Cargo, per abituarmi… Ecco mi sono abituato, Cargo è utilissimo quando il progetto non è banale come questi esempi 😉

Importante: Unlike let, you must declare the types of function arguments. OK, come con altri linguaggi, ma non tutti, come vedremo subito.

This is a deliberate design decision. While full-program inference is possible, languages which have it, like Haskell, often suggest that documenting your types explicitly is a best-practice. We agree that forcing functions to declare types while allowing for inference inside of function bodies is a wonderful sweet spot between full inference and no inference.

What about returning a value? Here’s a function that adds one to an integer:

fn add_one(x: i32) -> i32 {
    x + 1
}

Rust functions return exactly one value, and you declare the type after an ‘arrow’, which is a dash (-) followed by a greater-than sign (>). The last line of a function determines what it returns (come Lisp et al.). You’ll note the lack of a semicolon here, difatti:

fn main() {
    add_one(42);
}

fn add_one(x: i32) -> i32 {
    x + 1;
}

rs4-1
😳 correggo:

fn main() {
    let r = add_one(42);
    println!("{}", r);
}

fn add_one(x: i32) -> i32 {
    x + 1
}

rs4-2

Questo di sicuro rischio di dimenticarmelo 👿 Ma è OK, i vecchi si ricorderanno –forse– del Pascal, prima del Turbo 😉 E poi…

This reveals two interesting things about Rust: it is an expression-based language, and semicolons are different from semicolons in other ‘curly brace and semicolon’-based languages. These two things are related.

Espressioni contro istruzioni

Rust is primarily an expression-based language. There are only two kinds of statements, and everything else is an expression.

So what’s the difference? Expressions return a value, and statements do not. That’s why we end up with ‘not all control paths return a value’ here: the statement x + 1; doesn’t return a value. There are two kinds of statements in Rust: ‘declaration statements’ and ‘expression statements’. Everything else is an expression. Let’s talk about declaration statements first.

In Rust, however, using let to introduce a binding is not an expression. The following will produce a compile-time error:

let x = (let y = 5); // expected identifier, found keyword `let`

The compiler is telling us here that it was expecting to see the beginning of an expression, and a let can only begin a statement, not an expression.

Qui è da passarci un po’ di tempo su, forse è solo la terminologia e la sintassi ma è particolare, imho 🙄

Note that assigning to an already-bound variable (e.g. y = 5) is still an expression, although its value is not particularly useful. Unlike other languages where an assignment evaluates to the assigned value (e.g. 5 in the previous example), in Rust the value of an assignment is an empty tuple () because the assigned value can have just one owner, and any other returned value would be too surprising:

let mut y = 5;
let x = (y = 6);  // x has the value `()`, not `6`

The second kind of statement in Rust is the expression statement. Its purpose is to turn any expression into a statement. In practical terms, Rust’s grammar expects statements to follow other statements. This means that you use semicolons to separate expressions from each other. This means that Rust looks a lot like most other languages that require you to use semicolons at the end of every line, and you will see semicolons at the end of almost every line of Rust code you see.

What is this exception that makes us say “almost“? You saw it already, in this code:

fn add_one(x: i32) -> i32 {
    x + 1
}

Our function claims to return an i32, but with a semicolon, it would return () instead. Rust realizes this probably isn’t what we want, and suggests removing the semicolon in the error we saw before.
Quindi è ancora diverso dal Pascal antico 😉

Ritorni anticipati

But what about early returns? Rust does have a keyword for that, return:

fn foo(x: i32) -> i32 {
    return x;

    // we never run this code!
    x + 1
}

Using a return as the last line of a function works, but is considered poor style:

fn foo(x: i32) -> i32 {
    return x + 1;
}

The previous definition without return may look a bit strange if you haven’t worked in an expression-based language before, but it becomes intuitive over time. Yesss 😀

Funzioni non ritornanti

Non so tradurre diverging functions.
Rust has some special syntax for ‘diverging functions’, which are functions that do not return:

fn diverges() -> ! {
    panic!("This function never returns!");
}

panic! is a macro, similar to println!() that we’ve already seen. Unlike println!(), panic!() causes the current thread of execution to crash with the given message. Because this function will cause a crash, it will never return, and so it has the type ‘!’, which is read ‘diverges’.
Nota per me: devo correggere il post in cui ho misinterpretato il simbolo !.

If you add a main function that calls diverges() and run it, you’ll get some output that looks like this:

thread ‘<main>’ panicked at ‘This function never returns!’, hello.rs:2

If you want more information, you can get a backtrace by setting the RUST_BACKTRACE environment variable, non riporto l’esempio.
RUST_BACKTRACE also works with Cargo’s run command.

A diverging function can be used as any type:

let x: i32 = diverges();
let x: String = diverges();

Puntatori a funzione

We can also create variable bindings which point to functions:

let f: fn(i32) -> i32;

f is a variable binding which points to a function that takes an i32 as an argument and returns an i32. For example:

fn plus_one(i: i32) -> i32 {
    i + 1
}

// without type inference
let f: fn(i32) -> i32 = plus_one;

// with type inference
let f = plus_one;

We can then use f to call the function:

fn main() {
    fn plus_one(i: i32) -> i32 { i + 1 }
    let f = plus_one;
    let six = f(5);
    println!("{}", six)
}

rs4-3
:mrgreen:

Posta un commento o usa questo indirizzo per il trackback.

Trackback

  • Rust – il linguaggio – 2 | Ok, panico su 23 gennaio 2016 alle 09:35

    […] dal !. Correzione: il ! indica che la funzione println!() non ritorna un valore, come raccontato qui. Rust implements println! as a macro rather than a function for good reasons, but that’s an […]

  • Rust – il linguaggio – 5 | Ok, panico su 28 gennaio 2016 alle 08:43

    […] Oggi qui: /usr/local/share/doc/rust/html/book/primitive-types.html continuando da qui. […]

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Google photo

Stai commentando usando il tuo account Google. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.

%d blogger hanno fatto clic su Mi Piace per questo: