Más sobre impresión

En Rust se puede imprimir como se quiera. Por eso, interesa conocer algunas cosas más sobre este tema.

Si se añade \n se imprimirá una nueva línea. Si se añade \t se insertará un tabulador:

fn main() {
    // Observa: la función usada es print!, no println!
    print!("\tComienza con un tabulador\ny salta a una nueva línea");
}

El código anterior imprime lo siguiente:

        Comienza con un tabulador
y salta a una nueva línea

Dentro de "" se puede escribir en diferentes líneas, pero es necesario tener cuidado con los espacios:

fn main() {
    // Nota: después de la primera línea tienes que comenzar la siguiente línea
    // en la primera columna (pegado a la izquierda).
    println!("Dentro de comillas
se puede escribir
en muchas líneas
y se imprimirá correctamente.");
    // Si se escribe directamente bajo la sentencia, se añadirán los espacios
    // correspondientes de la izquierda
    println!("Si se olvida que hay que
    escribir pegado al lado izquierdo
    estos espacios se añadirán
    a la impresión.");
}

El código anterior imprime:

Dentro de comillas
se puede escribir
en muchas líneas
y se imprimirá correctamente.
Si se olvida que hay que
    escribir pegado al lado izquierdo
    estos espacios se añadirán
    a la impresión.

Si se necesitase imprimir caracteres como \n (caracteres de escape, como el del salto de línea y el tabulador que se han visto antes), se puede añadir un \ extra:

fn main() {
    println!("Se imprimen caracteres de escape, no inserta nueva línea y tabulador: \\n y \\t");
}

Lo que imprime:

Se imprimen caracteres de escape, no inserta nueva línea y tabulador: \n y \t

A veces se necesitan muchos " y caracteres de escape en el texto, por lo que Rust proporciona un método más simple para ignorarlos: se añade r# al comienzo y # al final de la cadena de caracteres.

fn main() {
    // En esta línea hemos usado \ cinco veces
    println!("Él dijo, \"Puedes encontrar el fichero en c:\\files\\my_documents\\file.txt\". Y así fue como lo encontré.");
    println!(r#"Él dijo, "Puedes encontrar el fichero en c:\files\my_documents\file.txt". Y así fue como lo encontré."#)
}

Ambas opciones imprimen lo mismo, pero el uso de r# lo hace más simple de entender.

Él dijo, "Puedes encontrar el fichero en c:\files\my_documents\file.txt". Y así fue como lo encontré.
Él dijo, "Puedes encontrar el fichero en c:\files\my_documents\file.txt". Y así fue como lo encontré.

Si se necesitara imprimir un carácter # en el texto, se puede usar r## al comienzo del texto y ## al final. Si se usaran más de dos consecutivos, se pueden seguir añadiendo # al comienzo y al final, hasta que no coincida con nada contenido en el texto.

fn main() {

    let my_string = "'Hola, mundo,' dijo."; // comilla simples
    let quote_string = r#""Hola, mundo," dijo."#; // comillas dobles
    // Contiene # se necesita al menos ##
    let hashtag_string = r##"El hasgtag #holamundo se ha hecho muy popular."##; 
    // Contiene ### se necesitan al menos ####
    let many_hashtags = r####""No se tiene que teclear ### para usar un hashtag. Solo hay que usar #.""####; 

    println!("{}\n{}\n{}\n{}\n", my_string, quote_string, hashtag_string, many_hashtags);
}

Este código imprimirá:

'Hola, mundo,' dijo.
"Hola, mundo," dijo.
El hasgtag #holamundo se ha hecho muy popular.
"No se tiene que teclear ### para usar un hashtag. Solo hay que usar #."

Existe otro uso para r#: usar palabras reservadas (como let, fn, etc.) como nombres de variable.

fn main() {
    let r#let = 6; // let como nombre de variable
    let mut r#mut = 10; // Esta variable se llama mut
}

Esta función de r# se introdujo porque las versiones más antiguas de Rust tenían menos palabras reservadas que ahora. Así se pueden evitar errores en código previo en el que se usaban nombres de variables que ahora son palabras reservadas.

Puede ser que por alguna razón realmente se necesite una función que se denomine como return. Así se puede escribir:

fn r#return() -> u8 {
    println!("Ahí va tu número.");
    8
}

fn main() {
    let mi_numero = r#return();
    println!("{}", mi_numero);
}

Que imprime:

Ahí va tu número.
8

Esto no se va a necesitar normalmente, pero si realmente hace falta, se puede usar.

Si se necesita imprimir los bytes de un &str o un char, basta con escribir la letra b delante de la cadena. Esto funciona para todos los caracteres ASCII. Estos son todos los caracteres ASCII.

☺☻♥♦♣♠♫☼►◄↕‼¶§▬↨↑↓→∟↔▲▼123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Así, este código:

fn main() {
    // N.T.: no se pueden poner tildes ya que en las 
    // vocales ya que las letras resultantes no forman
    // parte del conjunto de caracteres ASCII
    println!("{:?}", b"Esto aparece como una lista de numeros");
}

Da el siguiente resultado:

[69, 115, 116, 111, 32, 97, 112, 97, 114, 101, 99, 101, 32, 99, 111, 109, 111, 32, 117, 110, 97, 32, 108, 105, 115, 116, 97, 32, 100, 101, 32, 110, 117, 109, 101, 114, 111, 115]

Para un char esto se llama byte y para el tipo &str se llama byte de cadena de texto.

También existe un sistema de escape para insertar caracteres Unicode: \u{}. Así es posible imprimir cualquier carácter Unicode en una cadena de texto. Además, es posible formatear un número en hexadecimal usando {:X}. El siguiente ejemplo demuestra cómo imprimir el código hexadecimal que representa al carácter Unicode y cómo imprimirlo de nuevo como carácter.

fn main() {
    // Convierte el char a u32 para obtener su valor numérico
    println!("{:X}", '행' as u32); 
    println!("{:X}", 'H' as u32);
    println!("{:X}", '居' as u32);
    println!("{:X}", 'い' as u32);

    // Imprime los caracteres con el sistema de escape \u
    println!("\u{D589}, \u{48}, \u{5C45}, \u{3044}"); 
}

Ya se conoce que println! puede imprimir con {} (modo Display) t {:?} (para Depuración). Además de {:#?} para "impresión bonita". Pero existen otras muchas formas de imprimir.

Por ejempli, si se dispone de una referencia se puede usar {:p} para imprimir la dirección del puntero. Es decir, el lugar de la memoria del ordenador a la que apunta la referencia.

fn main() {
    let number = 9;
    let number_ref = &number;
    println!("{:p}", number_ref);
}

El código anterior imprime algo parecido a 0x7ffec9426f4c, dependerá de dónde se ejecute el programa y se almacene el número referenciado en la memoria del ordenador.

Los valores numéricos se pueden imprimir en binario, hexadecimal u octal:

fn main() {
    let number = 555;
    println!("Binario: {:b}, hexadecimal: {:x}, octal: {:o}", number, number, number);
}

Que imprime Binario: 1000101011, hexadecimal: 22b, octal: 1053.

También se pueden añadir números entre las llaves para indicar qué variable utilizar, teniendo en cuenta que la primera tiene como índice el 0, la segunda el 1 y así sucesivamente.

fn main() {
    let nombre_padre = "José Miguel";
    let nombre_hijo = "Víctor";
    let apellido = "González";
    println!("Este es {1} {2}, hijo de {0} {2}.", nombre_padre, nombre_hijo, apellido);
}

La variable nombre_padre está en la posición 0, nombre_hijoen la 1 y apellido está en la posición 2. Por eso, el código anterior imprime Este es Víctor González, hijo de José Miguel González..

Puede suceder que sea necesario imprimir una cadena de caracteres compleja con muchas variables dentro de las llaves. O puede que se necesite imprimir la misma variable dos o más veces. Para ello, se pueden añadir nombres a las llaves.

fn main() {
    println!(
        "{city1} está en {pais} y {city2} también está en {pais},
pero {city3} no está en {pais}.",
        city1 = "Seul",
        city2 = "Busan",
        city3 = "Tokio",
        pais = "Korea"
    );
}

Que imprime:

Seul está en Korea y Busan también está en Korea,
pero Tokio no está en Korea.

También es posible editar de forma compleja el formato de la impresión. Tiene esta forma:

{variable:relleno alineamiento mínimo.máximo}

Para entender esta sintaxis:

  1. ¿Se quiere usar un nombre de variable? Se escribe primero su nombre, como antes en {pais}. Lo siguiente será añadir (opcionalmente) : después si se quiere formatear de algún modo.
  2. ¿Se se necesita un carácter de relleno? Por ejemplo, 55 con tres "ceros de relleno" se imprimiría como 00055.
  3. ¿Que alineamiento se necesita para el relleno? izquierda, centro o derecha.
  4. ¿Se desea una longitud mínima? solo hay que indicar el número deseado.
  5. ¿Se desea una longitud máxima? solo hay que indicarla con un . delante.

Por ejemplo, si se quiere escribir la letra "a" con cinco caracteres - de relleno (a izquierda y derecha):

fn main() {
    let letra = "a";
    println!("{:-^11}", letra);
}

Que imprime -----a-----. El ordenador lo interpreta así:

  • ¿Hay un nombre de variable? No, ya que lo primero que aparece en {:-^11} son los dos puntos. No hay nombre de variable delante de estos dos puntos.
  • ¿Se pide un carácter de relleno? Sí, - está justo después de : y lo sigue un ^, lo que significa que el texto se inserta en el centro y el carácter de relleno se reparte en los espacios sobrantes a izquierda y derecha. Las otras dos posibilidades son: < que indica que el texto va a la izquierda y el carácter de relleno a la izquierda, y > que indica que el texto va a la derecha con el relleno a la izquierda.

A continuación, se muestran diversos ejemplos de tipos de formateo.

fn main() {
    let titulo = "NOTICIAS DE HOY";
    // sin variable, relleno con -, centrado, longitud de 30 caracteres
    println!("{:-^30}", titulo); 
    let barra = "|";
    // sin variable, relleno con espacios, 15 caracters cada uno, una barra a izquierda y otra a derecha
    println!("{: <15}{: >15}", barra, barra); 
    let a = "SEUL";
    let b = "TOKIO";
     // variables city1 y city2, relleno con -, a izquierda y a derecha
     println!("{city1:-<15}{city2:->15}", city1 = a, city2 = b);
}

El código anterior imprime:

-------NOTICIAS DE HOY--------
|                            |
SEUL---------------------TOKIO