Imprimiendo '¡Hola, mundo!'

Puedes ver este capítulo en YouTube en inglés: Video 1, Video 2

Cuando creas un nuevo programa Rust, siempre contiene este código:

fn main() {
    println!("Hello, world!");
}
  • fn significa función.
  • main es el nombre de la función que inicia el programa.
  • () significa que en este caso no se le pasan variables a esta función.
  • {} es un bloque de código. Es donde se encuentra el código.
  • println! es una macro, que es como una función que sirve para escribir código por ti. Las macros siempre tienen un ! al final de su nombre. Por ahora, recuerda que ! significa que es una macro.

Para aprender lo que hace ;, crearemos otra función. Primero, en main imprimiremos el número 81.

1 N.T.: aprovechamos para cambiar el saludo a español. En Rust, cuando se utiliza la aplicación cargo para crear un programa, siempre se incorpora el código de main con "Hello, world!" de forma automática.

fn main() {
    println!("¡Hola, mundo número {}!", 8);
}

Las {} dentro de println! indican a Rust que "ponga la variable en este lugar". Este código imprime ¡Hola, mundo número 8!.

Podemos poner más cosas ampliando lo anterior.

fn main() {
    println!("¡Hola, mundos número {} y {}!", 8, 9);
}

El codigo anterior imprime ¡Hola, mundos número 8 y 9!.

Ahora vamos a crear una función.

fn numero() -> i32 {
    8
}

fn main() {
    println!("¡Hola, mundo número {}!", numero());
}

El código anterior también imprime ¡Hola, mundo número 8!. Cuando Rust encuentra numero() entiende que es una función. Esta función:

  • No toma ningún parámetro (porque tiene una llamada con ()).
  • Devuelve un i32. El símbolo de flecha -> muestra el tipo que devuelve la función.
  • La función en sí misma solo contiene un 8. Al no terminar en ; este valor es el que devuelve al terminar de ejecutarse. Si tuviera un ; detrás, la función no devolvería nada (devolvería un ()). Rust no compilaría si contuviera un ; al final ya que se ha indicado que (tras la flecha) debe devolver un valor de tipo i32 y con el ; se devuelve () que no es de tipo i32.
fn numero() -> i32 {
    8;
}

fn main() {
    println!("¡Hola, mundo número {}!", numero());
}
error[E0308]: mismatched types
 --> src/main.rs:1:16
  |
1 | fn numero() -> i32 {
  |    ------      ^^^ expected `i32`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
2 |     8;
  |      - help: consider removing this semicolon

Esto significa que "me dijiste que numero() devuelve un i32, pero añadiste un ; por lo que esta función no devuelve nada". Así que el compilador sugiere que se elimine el punto y coma.

También se puede escribir lo siguiente return 8;, pero en Rust lo normal es simplemente eliminar el ; para ejecutar el return.

Cuando se quiere pasar variables a una función, se deben poner dentro de (). Hay que darles un nombre e indicar su tipo.

// Entran a la función dos i32s. Se llaman num_uno y num_dos
fn multiplicar(num_uno: i32, num_dos: i32) {
    let resultado = num_uno * num_dos;
    println!("{} por {} es {}", num_uno, num_dos, resultado);
}

fn main() {
    multiplicar(8, 9); // Pasamos unos números directamente
    let algun_numero = 10; // o podemos declarar dos variables
    let algun_otro_numero = 2;
    multiplicar(algun_numero, algun_otro_numero); // y pasarlos a la función
}

También se puede devolver un i32. Basta con poner la variable resultado como la última de la función sin ; al final.

fn multiplicar(num_uno: i32, num_dos: i32) -> i32 {
    let resultado = num_uno * num_dos;
    println!("{} por {} es {}", num_uno, num_dos, resultado);
    resultado // este es el valor i32 que se retorna
}

fn main() {
    // multiplicar() imprime el resultado y lo devuelve,
    // lo que permite asignarlo a resultado_mult
    let resultado_mult = multiplicar(8, 9);
}

La declaración de variables y los bloques de código

Se usa letpara declarar una variable (para decirle a Rust que construya una variable).

fn main() {
    let mi_numero = 8;
    println!("¡Hola, mundo número {}!", mi_numero);
}

Las variables existen dentro de un bloque de código {}. En el siguiente ejemplo mi_numero desaparece antes de llamar a println! porque se encuentra dentro de su propio código.

fn main() {
    {
        let mi_numero = 8; // mi_numero se crea aquí
                           // mi_numero se extingue aquí
    }
    println!("¡Hola, mundo número {}!", mi_numero);// ⚠️  mi_numero no existe y
                                                   // println!() no lo puede encontrar
}

Se puede usar un bloque de código para devolver un valor, como en el siguiente código.

fn main() {
    let mi_numero = {
        let segundo_num = 8;
        segundo_num + 9 // sin punto y coma, por lo que el
                        // bloque de código devuelve 8 + 9 = 17
    }; 
    println!("¡Hola, mundo número {}!", mi_numero);
}

Si se añadiera un punto y coma en la sentencia final del bloque, devolvería () (nada).

fn main() {
    let mi_numero = {
        let segundo_num = 8; // declara el segundo número
        segundo_num + 9;     // suma 9 con el segundo número
                             // pero no se devuelve
                             // segundo_num desaparece aquí
    }; 
    println!("¡Hola, mundo número {:?}!", mi_numero); // mi_numero es ()
}

Si has observado bien, hemos cambiado {} por {:?}. El motivo se verá en el siguiente capítulo.