Atributos
Ya se ha visto anteriormente código como este #[derive(Debug)]
. Este tipo de código es un atributo. Los atributos son pequeñas piezas de código que dan información al compilador. No son fáciles de crear, pero son muy fáciles de usar. Un atributo puede comenzar con solo #
, lo que significa que solo afecta al código de la siguiente línea. Sin embargo, si comienza con #!
afectará a todo lo que esté en su espacio.
Hay atributos que aparecen mucho:
#[allow(dead_code)]
y #[allow(unused_variables)]
. Si el fichero contiene código que no se utiliza, Rust compilará, pero avisará. Por ejemplo, el siguiente código contiene un struct
vacío y una variable. Ninguno se usa en el código:
struct JustAStruct {} fn main() { let some_char = 'ん'; }
Rust se queja e indica que no se usan:
warning: unused variable: `some_char`
--> src\main.rs:4:9
|
4 | let some_char = 'ん';
| ^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_some_char`
|
= note: `#[warn(unused_variables)]` on by default
warning: struct is never constructed: `JustAStruct`
--> src\main.rs:1:8
|
1 | struct JustAStruct {}
| ^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
Se ha visto que se puede utilizar un guión bajo para indicar que no se usa _
:
struct _JustAStruct {} fn main() { let _some_char = 'ん'; }
Pero también se puede utilizar un atributo. El propio mensaje de Rust indica que tiene activo los atributos #[warn(unused_variables)]
y #[warn(dead_code)]
. En el código anterior, JustAStruct
es código muerto y some_char
es una variable sin usar. El atributo opuesto a warn
es allow
, por lo que se puede escribire en el código y Rust dejará de avisar para estos casos:
#![allow(dead_code)] #![allow(unused_variables)] struct Struct1 {} // Crea cinco structs struct Struct2 {} struct Struct3 {} struct Struct4 {} struct Struct5 {} fn main() { let char1 = 'ん'; // y cuatro variables. No se usa ninguno de ellos, pero el compilador ya no da ningún mensaje let char2 = ';'; let some_str = "I'm just a regular &str"; let some_vec = vec!["I", "am", "just", "a", "vec"]; }
Es importante tener en cuenta el código muerto y las variables sin uso, pero en ocasiones puede ser necesario que el compilador deje de avisar durante un tiempo. Se puede necesitar desarrollar el código o enseñar a alguien y no se quiere confundir con excesivos mensajes.
El atributo #[derive(NombreDeRasfgo)]
permite derivar algunos rasgos para los struct y enum que se creen. Diversos rasgos de uso común, como Debug
, pueden derivarse de esta forma. Otros, como Display
, no se pueden derivar. En el caso de Display
es necesari que se dedica por parte del desarrollador cómo se quiere mostrar el elemento.
// ⚠️ #[derive(Display)] struct HoldsAString { the_string: String, } fn main() { let my_string = HoldsAString { the_string: "¡Aquí estoy!".to_string(), }; }
El mensaje de error lo indica bien claro:
error: cannot find derive macro `Display` in this scope
--> src/main.rs:2:10
|
2 | #[derive(Display)]
| ^^^^^^^
Pero en los casos de rasgos que se pueden derivar, se pueden indicar tantos como se necesite. En el siguiente ejemplo se le dan siete rasgos diferentes a un struct
, solo por gusto, aunque solo se necesite uno en este caso.
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Clone)] struct HoldsAString { the_string: String, } fn main() { let my_string = HoldsAString { the_string: "¡Aquí estoy!".to_string(), }; println!("{:?}", my_string); }
Se puede derivar Copy
, pero solo en los casos que un elemento solo contiene elementos que también tienen el rasgo Copy
. En el ejemplo anterior no es posible puesto que String
no tiene dicho rasgo y el struct HodsAString
contiene una variable de dicho tipo. Sin embargo en el siguiente ejemplo sí se puede derivar Copy
al contener tipos que disponen de dicho rasgo:
#[derive(Clone, Copy)] // Se necesita Clone para usar Copy struct NumberAndBool { number: i32, // i32 es Copy true_or_false: bool // bool es también Copy. Por lo que no es problema } fn does_nothing(input: NumberAndBool) { } fn main() { let number_and_bool = NumberAndBool { number: 8, true_or_false: true }; does_nothing(number_and_bool); does_nothing(number_and_bool); // Sin Copy, esta fila daría error }
El atributo #[cfg()]
significa configuración e indica al compilador si ejecutar código o no. Normalmente, se encuentra de la siguiente forma #[cfg(test)]
. Se usa cuando se escriben funciones de prueba para que el compilador solo las ejecute cuando se está probando. Así, el código de prueba puede estar junto al código del programa sin que el compilador lo ejecute, salvo cuando se le indica.
Otro ejemplo del uso de cfg
es #[cfg(target_os = "windows")]
. Que indica que solo se ejecute el código en windows (o linux u otro sistema).
El atributo #![no_std]
indica a Rust que no incorpore la librería estándar. Esto implica que no se dispone de Vec
, String
y todo lo que aporta esta librería. Es útil cuando no es necesaria y el código tiene que ejecutarse en pequeños dispositivos con poca memoria.
Los diferentes atributos disponibles se pueden consultar aquí.