Rust es un lenguaje de programación de propósito general que enfatiza el rendimiento , la seguridad de tipos y la concurrencia . Aplica la seguridad de la memoria , lo que significa que todas las referencias apuntan a una memoria válida. Lo hace sin un recolector de basura tradicional ; en cambio, tanto los errores de seguridad de la memoria como las carreras de datos se evitan mediante el "verificador de préstamos", que rastrea la duración de vida de los objetos de las referencias en el momento de la compilación .
Rust no impone un paradigma de programación , sino que fue influenciado por ideas de la programación funcional , incluyendo inmutabilidad , funciones de orden superior , tipos de datos algebraicos y coincidencia de patrones . También admite la programación orientada a objetos a través de estructuras, enumeraciones , rasgos y métodos. Es popular para la programación de sistemas . [13] [14] [15]
El desarrollador de software Graydon Hoare creó Rust como un proyecto personal mientras trabajaba en Mozilla Research en 2006. Mozilla patrocinó oficialmente el proyecto en 2009. En los años posteriores a la primera versión estable en mayo de 2015, Rust fue adoptado por empresas como Amazon , Discord , Dropbox , Google ( Alphabet ), Meta y Microsoft . En diciembre de 2022, se convirtió en el primer lenguaje distinto de C y ensamblador en ser compatible con el desarrollo del kernel de Linux .
Rust se ha destacado por su rápida adopción y ha sido estudiado en investigaciones sobre la teoría del lenguaje de programación .
Rust comenzó como un proyecto personal en 2006 por el empleado de Mozilla Graydon Hoare. [16] Hoare ha declarado que Rust recibió su nombre por el grupo de hongos que están "sobrediseñados para sobrevivir". [16] Durante el período de tiempo entre 2006 y 2009, Rust no se hizo público a otros en Mozilla y fue escrito en el tiempo libre de Hoare; [17] Hoare comenzó a hablar sobre el lenguaje alrededor de 2009 después de que un pequeño grupo en Mozilla se interesara en el proyecto. [18] Hoare enfatizó la importancia de priorizar las buenas ideas de lenguajes antiguos sobre el desarrollo de nuevos, citando lenguajes como CLU (1974), BETA (1975), Mesa (1977), NIL (1981), Erlang (1987), Newsqueak (1988), Napier (1988), Hermes (1990), Sather (1990), Alef (1992) y Limbo (1996) como influencias, afirmando que "muchos lenguajes antiguos [son] mejores que los nuevos", y describiendo el lenguaje como "tecnología del pasado que viene a salvar al futuro de sí mismo". [17] [18] El primer desarrollador de Rust, Manish Goregaokar, describió de manera similar a Rust como basado en "investigaciones de hace principalmente décadas". [16]
Durante los primeros años, el compilador de Rust se escribió en aproximadamente 38.000 líneas de OCaml . [17] [19] Los primeros Rust contenían características como programación orientada a objetos explícita a través de una obj
palabra clave (más tarde eliminada) y un sistema para algo llamado typestates que permitiría rastrear las variables de un tipo junto con los cambios de estado (como pasar de no inicializado a inicializado). Las funciones eran puras de forma predeterminada, lo que significa que los efectos secundarios (como leer o escribir en un archivo) no estaban permitidos sin una anotación explícita. [17]
Mozilla patrocinó oficialmente el proyecto Rust en 2009. [16] Brendan Eich y otros ejecutivos, intrigados por la posibilidad de usar Rust como motor de navegación web seguro , colocaron ingenieros en el proyecto, incluidos Patrick Walton, Niko Matsakis, Felix Klock y Manish Goregaokar. Una sala de conferencias ocupada por los desarrolladores del proyecto fue bautizada como "la cueva de los nerds", con un cartel colocado afuera de la puerta. [16]
Durante este período de tiempo, el trabajo se había desplazado del compilador OCaml inicial a un compilador autohospedado , es decir , escrito en Rust, basado en LLVM . [20] [nota 4] El sistema de propiedad de Rust también estaba en su lugar en 2010. [16] El logotipo de Rust se desarrolló en 2011 basado en un plato de bicicleta . [22]
El primer lanzamiento público, Rust 0.1, se lanzó en enero de 2012. [20] [ Se necesita una mejor fuente ] A principios de la década de 2010, hubo una creciente participación de voluntarios de código abierto fuera de Mozilla y fuera de los Estados Unidos. En Mozilla, los ejecutivos terminaron empleando a más de una docena de ingenieros para trabajar en Rust a tiempo completo durante la siguiente década. [16]
Los años de 2012 a 2015 estuvieron marcados por cambios sustanciales en el sistema de tipos de Rust , especialmente, la eliminación del sistema de estado de tipo, la consolidación de otras características del lenguaje y la eliminación del recolector de basura . [17] [16] La gestión de memoria a través del sistema de propiedad se consolidó y amplió gradualmente para evitar errores relacionados con la memoria. Para 2013, la función del recolector de basura rara vez se usaba y el equipo la eliminó a favor del sistema de propiedad. [16] Otros cambios durante este tiempo incluyeron la eliminación de funciones puras , que se declaraban mediante una pure
anotación explícita, en marzo de 2013. [23] El soporte de sintaxis especializada para canales y varios tipos de puntero se eliminaron para simplificar el lenguaje. [17]
La expansión y consolidación de Rust estuvo influenciada por desarrolladores que venían de C++ (por ejemplo, rendimiento de características de bajo nivel), lenguajes de scripting (por ejemplo, Cargo y gestión de paquetes) y programación funcional (por ejemplo, desarrollo de sistemas de tipos). [17]
Graydon Hoare dejó Rust en 2013. [16] Esto le permitió evolucionar orgánicamente bajo una estructura de gobernanza más federada, con un "equipo central" de inicialmente 6 personas, 30-40 desarrolladores en total en varios otros equipos y un proceso de Solicitud de comentarios (RFC) para nuevas características del lenguaje agregado en marzo de 2014. [17] El equipo central crecería a 9 personas en 2016 con más de 1600 RFC propuestas. [17]
En enero de 2014, el editor en jefe de Dr. Dobb's Journal , Andrew Binstock, comentó sobre las posibilidades de Rust de convertirse en un competidor de C++ , junto con D , Go y Nim (entonces Nimrod). Según Binstock, si bien Rust era "ampliamente visto como un lenguaje notablemente elegante", la adopción se ralentizó porque cambió radicalmente de una versión a otra. [24] El desarrollo de Rust en este momento se centró en finalizar las características del lenguaje y avanzar hacia la versión 1.0 para poder lograr compatibilidad con versiones anteriores y convertir el lenguaje en un producto para su posible adopción en la industria. [17]
Seis años después de que Mozilla patrocinara su desarrollo, la primera versión estable , Rust 1.0, se publicó el 15 de mayo de 2015. [16] Un año después del lanzamiento, el compilador Rust había acumulado más de 1400 colaboradores y había más de 5000 bibliotecas de terceros publicadas en el sitio web de gestión de paquetes de Rust Crates.io. [17]
El desarrollo del motor del navegador Servo continuó en paralelo con Rust. Los equipos detrás de los dos proyectos trabajaron en estrecha colaboración; el equipo Servo probó las nuevas características de Rust y las nuevas características de Servo se utilizaron para brindar retroalimentación al equipo de Rust. [17] La primera versión de Servo se lanzó en 2016. [16] El navegador web Firefox se envió con código Rust a partir de 2016 (versión 45); [17] [25] , pero los componentes de Servo no aparecieron en Firefox hasta septiembre de 2017 (versión 57) como parte de los proyectos Gecko y Quantum . [26]
Se realizaron mejoras al ecosistema de la cadena de herramientas de Rust durante los años posteriores a la versión 1.0, incluido Rustfmt, la integración del entorno de desarrollo integrado , un ciclo regular de prueba y lanzamiento del compilador, un código de conducta de la comunidad y una discusión comunitaria organizada a través de un chat de IRC . [17]
La primera adopción fuera de Mozilla fue por parte de proyectos individuales en Samsung , Facebook (ahora Meta Platforms ), Dropbox y otros, incluida Tilde, Inc. (la empresa detrás de ember.js ). [17] [16] Amazon Web Services siguió en 2020. [16] Los ingenieros citaron el rendimiento, la falta de un recolector de basura, la seguridad y la comodidad de trabajar en el lenguaje como razones para la adopción, aunque reconocieron que era una apuesta arriesgada ya que Rust era una tecnología nueva. Los desarrolladores de Amazon citaron el hecho de que Rust usa la mitad de electricidad que un código similar escrito en Java , solo por detrás de C , [16] como lo encontró un estudio en la Universidad de Minho , la Universidad NOVA de Lisboa y la Universidad de Coimbra . [27] [nota 5]
En agosto de 2020, Mozilla despidió a 250 de sus 1.000 empleados en todo el mundo, como parte de una reestructuración corporativa causada por la pandemia de COVID-19 . [28] [29] El equipo detrás de Servo se disolvió. El evento generó preocupaciones sobre el futuro de Rust, debido a la superposición entre los dos proyectos. [30] En la semana siguiente, el equipo central de Rust reconoció el severo impacto de los despidos y anunció que los planes para una fundación Rust estaban en marcha. El primer objetivo de la fundación sería tomar posesión de todas las marcas comerciales y nombres de dominio , y asumir la responsabilidad financiera de sus costos. [31]
El 8 de febrero de 2021, cinco empresas fundadoras anunciaron la formación de la Fundación Rust: Amazon Web Services , Google , Huawei , Microsoft y Mozilla . [32] [33] La fundación, dirigida por Shane Miller durante sus primeros dos años, ofreció subvenciones de 20.000 dólares y otro tipo de apoyo a los programadores que trabajaran en las principales funciones de Rust. [16] En una publicación de blog publicada el 6 de abril de 2021, Google anunció el apoyo a Rust dentro del Proyecto de código abierto de Android como alternativa a C/C++. [34]
El 22 de noviembre de 2021, el equipo de moderación, responsable de hacer cumplir el código de conducta de la comunidad, anunció su renuncia "en protesta porque el equipo central se ha puesto a sí mismo al margen de cualquier responsabilidad". [35] En mayo de 2022, el equipo central de Rust, otros programadores principales y ciertos miembros de la junta directiva de la Fundación Rust implementaron reformas de gobernanza en respuesta al incidente. [36]
El 6 de abril de 2023, la Fundación Rust publicó un borrador de una nueva política de marca registrada, que incluía reglas sobre cómo se pueden usar el logotipo y el nombre de Rust, lo que generó reacciones negativas de los usuarios y colaboradores de Rust. [37]
La sintaxis de Rust es similar a la de C y C++, [38] [39] aunque muchas de sus características fueron influenciadas por lenguajes de programación funcional como OCaml . [40] Hoare ha descrito a Rust como dirigido a desarrolladores de C++ frustrados y enfatizó características como la seguridad, el control del diseño de la memoria y la concurrencia . [18] La seguridad en Rust incluye las garantías de seguridad de la memoria, seguridad de tipos y falta de carreras de datos.
A continuación se muestra un programa "¡Hola, mundo!" en Rust. La fn
palabra clave denota una función y la println!
macro (consulte § Macros) imprime el mensaje en la salida estándar . [41] Las instrucciones en Rust se separan con punto y coma .
fn main () { println! ( "¡Hola, mundo!" ); }
Las variables en Rust se definen a través de la let
palabra clave. [42] El siguiente ejemplo asigna un valor a la variable con nombre foo
.
fn main () { let foo = 10 ; println! ( "El valor de foo es {foo}" ); }
Las variables son inmutables de forma predeterminada, y agregar la mut
palabra clave permite que la variable se mute. [43] El siguiente ejemplo utiliza //
, que denota el comienzo de un comentario . [44]
fn main () { let mut foo = 10 ; // Este código no se compilaría sin agregar "mut". println! ( "El valor de foo es {foo}" ); foo = 20 ; println! ( "El valor de foo es {foo}" ); }
Varias let
expresiones pueden definir múltiples variables con el mismo nombre, lo que se conoce como sombreado de variables . El sombreado de variables permite transformar variables sin tener que nombrarlas de manera diferente. [45] El siguiente ejemplo declara una nueva variable con el mismo nombre que es el doble del valor original:
fn main () { let foo = 10 ; println! ( "El valor de foo es {foo}" ); let foo = foo * 2 ; println! ( "El valor de foo es {foo}" ); }
El sombreado variable también es posible para valores de diferentes tipos, desde una cadena hasta su longitud:
fn main () { let espacios = " " ; let espacios = espacios . len (); }
En Rust, los bloques de código están delimitados por llaves . [46]
if
bloquesUna if
expresión condicional ejecuta código en función de si el valor dado es true
. else
se puede utilizar cuando el valor se evalúa como false
, y se puede utilizar para combinar múltiples expresiones. [47]else if
fn main () { let x = 10 ; if x > 5 { println! ( "el valor es mayor que cinco" ); } si x % 7 == 0 { println! ( "el valor es divisible por 7" ); } de lo contrario si x % 5 == 0 { println! ( "el valor es divisible por 5" ); } de lo contrario { println! ( "el valor no es divisible por 7 o 5" ); } }
while
bucleswhile
se puede utilizar para repetir un bloque de código mientras se cumple una condición. [48]
fn main () { // Iterar sobre todos los números enteros del 4 al 10 let mut valor = 4 ; while valor <= 10 { println! ( "valor = {valor}" ); valor += 1 } }
for
bucles e iteradoresLos bucles for en Rust recorren los elementos de una colección. [49] Las expresiones "for" funcionan sobre cualquier tipo de iterador .
fn main (){ // Uso de `for` con sintaxis de rango para la misma funcionalidad que la anterior para el valor en 4 ..= 10 { println! ( "valor = {valor}" ); } }
En el código anterior, 4..=10
hay un valor de tipo Range
que implementa la Iterator
característica. El código entre llaves se aplica a cada elemento devuelto por el iterador.
Los iteradores se pueden combinar con funciones sobre iteradores como map
, filter
, y sum
. Por ejemplo, la siguiente suma todos los números entre 1 y 100 que son múltiplos de 3:
( 1 ..= 100 ) .filtrar ( |& x | x % 3 == 0 ).suma ( )
loop
y break
declaracionesEn términos más generales, la loop
palabra clave permite repetir una parte del código hasta que break
se produzca un valor. break
Opcionalmente, puede salir del bucle con un valor. Las etiquetas indicadas con se pueden utilizar para interrumpir un bucle externo cuando los bucles están anidados. [50]'label_name
fn main () { let valor = 456 ; let mut x = 1 ; let y = loop { x *= 10 ; if x > valor { break x / 10 ; } }; println! ( "mayor potencia de diez que sea menor o igual que valor: {y}" ); deje que mut suba = 1 ; ' externo : bucle { deje que mut baje = 120 ; bucle { si arriba > 100 { romper 'externo ; } si abajo < 4 { romper ; } abajo /= 2 ; arriba += 1 ; println! ( "arriba: {arriba}, abajo: {abajo}" ); } arriba *= 2 ; } }
Rust está orientado a expresiones , y casi todas las partes del cuerpo de una función son expresiones , incluidos los operadores de flujo de control. [51] La if
expresión se utiliza para proporcionar el operador condicional ternario . Como los retornos son implícitos, una función no necesita terminar con una return
expresión; si se omite el punto y coma, el valor de la última expresión en la función se utiliza como valor de retorno , [52] como se ve en la siguiente implementación recursiva de la función factorial :
fn factorial ( i : u64 ) -> u64 { si i == 0 { 1 } de lo contrario { i * factorial ( i - 1 ) } }
La siguiente implementación iterativa..=
utiliza el operador para crear un rango inclusivo:
fn factorial ( i : u64 ) -> u64 { ( 2 ..= i ) .producto () }
Las expresiones match
and se pueden utilizar para la búsqueda de patrones . Por ejemplo, se puede utilizar para duplicar un valor entero opcional si está presente y devolver cero en caso contrario: [53]if let
match
fn double ( x : Opción < u64 > ) -> u64 { coincidir x { Alguna ( x ) => x * 2 , Ninguna => 0 , } }
De manera equivalente, esto se puede escribir con y :if let
else
fn double ( x : Opción < u64 > ) -> u64 { si deja Alguna ( x ) = x { x * 2 } de lo contrario { 0 } }
Rust es un lenguaje fuertemente tipado y estático . Los tipos de todas las variables deben conocerse en el momento de la compilación; asignar un valor de un tipo particular a una variable con un tipo diferente provoca un error de compilación . La inferencia de tipos se utiliza para determinar el tipo de las variables si no se especifica. [54]
El tipo entero predeterminado es , y el tipo de punto flotantei32
predeterminado es . Si el tipo de un número literal no se proporciona explícitamente, se infiere del contexto o se utiliza el tipo predeterminado. [55]f64
Los tipos enteros en Rust se nombran en función de su signo y la cantidad de bits que ocupa el tipo. Por ejemplo, i32
es un entero con signo que ocupa 32 bits de almacenamiento, mientras que u8
no tiene signo y solo ocupa 8 bits de almacenamiento. isize
y usize
ocupan espacio de almacenamiento según la arquitectura de la computadora que ejecuta el código; por ejemplo, en computadoras con arquitecturas de 32 bits , ambos tipos ocuparán 32 bits de espacio.
De manera predeterminada, los literales enteros están en base 10, pero se admiten diferentes bases0b11
con prefijos, por ejemplo, para números binarios , 0o567
para octales y 0xDB
para hexadecimales . De manera predeterminada, los literales enteros tienen como valor predeterminado su tipo. Se pueden usar i32
sufijos como para establecer explícitamente el tipo de un literal. [56] Los literales de byte como están disponibles para representar el valor ASCII (en ) de un carácter específico. [57]4u32
b'X'
u8
El tipo booleano se conoce como bool
que puede tomar un valor de true
o false
. A char
ocupa 32 bits de espacio y representa un valor escalar Unicode: un punto de código Unicode que no es un sustituto . [58] Los números de punto flotante IEEE 754 son compatibles con f32
para flotantes de precisión simple y f64
para flotantes de precisión doble . [59]
Los tipos definidos por el usuario se crean con las palabras clave struct
or enum
. La struct
palabra clave se utiliza para indicar un tipo de registro que agrupa múltiples valores relacionados. [60] enum
Los s pueden adoptar diferentes variantes en tiempo de ejecución, con capacidades similares a los tipos de datos algebraicos que se encuentran en los lenguajes de programación funcional. [61] Tanto las estructuras como las enumeraciones pueden contener campos con diferentes tipos. [62] Se pueden definir nombres alternativos para el mismo tipo con la type
palabra clave. [63]
La impl
palabra clave puede definir métodos para un tipo definido por el usuario. Los datos y las funciones se definen por separado. Las implementaciones cumplen una función similar a la de las clases en otros lenguajes. [64]
Option
Los valores se manejan utilizando azúcar sintáctica , como la if let
construcción, para acceder al valor interno (en este caso, una cadena): [80]
fn main () { let nombre1 : Option <& str > = None ; // En este caso, no se imprimirá nada si let Some ( nombre ) = nombre1 { println! ( "{nombre}" ); } let name2 : Option <& str > = Some ( "Matthew" ); // En este caso, se imprimirá la palabra "Matthew" si let Some ( name ) = name2 { println! ( "{name}" ); } }
Rust no utiliza punteros nulos para indicar la falta de datos, ya que hacerlo puede provocar una desreferenciación nula . Por consiguiente, se garantiza que las referencias básicas &
y no sean nulas. En cambio, Rust utiliza para este propósito: indica que hay un valor presente y es análogo al puntero nulo. [81] implementa una "optimización de puntero nulo", evitando cualquier sobrecarga espacial para tipos que no pueden tener un valor nulo (referencias o los tipos, por ejemplo). [82]&mut
Option
Some(T)
None
Option
NonZero
A diferencia de las referencias, los tipos de punteros sin formato *const
y *mut
pueden ser nulos; sin embargo, es imposible desreferenciarlos a menos que el código se declare explícitamente inseguro mediante el uso de un unsafe
bloque. A diferencia de la desreferenciación, la creación de punteros sin formato está permitida dentro del código seguro de Rust. [83]
Rust no ofrece conversión de tipos implícita (coerción) entre tipos primitivos. Sin embargo, se puede realizar una conversión de tipos explícita (casting) utilizando la as
palabra clave. [84]
sea x = 1000 ; println! ( "1000 como u16 es: {}" , x como u16 );
El sistema de propiedad de Rust consiste en reglas que garantizan la seguridad de la memoria sin usar un recolector de basura. En tiempo de compilación, cada valor debe estar asociado a una variable llamada el propietario de ese valor, y cada valor debe tener exactamente un propietario. [85] Los valores se mueven entre diferentes propietarios a través de la asignación o pasando un valor como un parámetro de función. Los valores también pueden tomarse prestados, lo que significa que se pasan temporalmente a una función diferente antes de ser devueltos al propietario. [86] Con estas reglas, Rust puede evitar la creación y el uso de punteros colgantes : [86] [87]
fn print_string ( s : Cadena ) { println! ( "{}" , s ); } fn main () { let s = String :: from ( "Hola, Mundo" ); print_string ( s ); // s consumido por print_string // s se ha movido, por lo que ya no se puede usar // otro print_string(s); generaría un error de compilación }
Debido a estas reglas de propiedad, los tipos de Rust se conocen como tipos lineales o afines , lo que significa que cada valor se puede usar exactamente una vez. Esto impone una forma de aislamiento de fallas de software , ya que el propietario de un valor es el único responsable de su corrección y desasignación. [88]
Cuando un valor queda fuera del ámbito, se elimina ejecutando su destructor . El destructor puede definirse mediante programación mediante la implementación del Drop
rasgo. Esto ayuda a administrar recursos como identificadores de archivos, sockets de red y bloqueos , ya que cuando se eliminan objetos, los recursos asociados con ellos se cierran o liberan automáticamente. [89]
La duración de vida de un objeto se refiere al período de tiempo durante el cual una referencia es válida; es decir, el tiempo entre la creación y la destrucción del objeto. [90] Estas duraciones de vida están asociadas implícitamente con todos los tipos de referencia de Rust. Si bien a menudo se infieren, también se pueden indicar explícitamente con parámetros de duración de vida nombrados (a menudo denotados como 'a
, 'b
, etc.). [91]
Los tiempos de vida en Rust pueden considerarse de alcance léxico , lo que significa que la duración de la vida de un objeto se infiere del conjunto de ubicaciones en el código fuente (es decir, números de función, línea y columna) para los que una variable es válida. [92] Por ejemplo, una referencia a una variable local tiene un tiempo de vida correspondiente al bloque en el que está definida: [92]
fn main () { let x = 5 ; // ------------------+- Tiempo de vida 'a // | let r = & x ; // -+-- Tiempo de vida 'b | // | | println! ( "r: {}" , r ); // | | // | | // -+ | } // ------------------+
El verificador de préstamos del compilador de Rust luego hace cumplir que las referencias solo se usen en las ubicaciones del código fuente donde el tiempo de vida asociado sea válido. [93] [94] En el ejemplo anterior, almacenar una referencia a variable x
en r
es válido, ya que variable x
tiene un tiempo de vida más largo ( 'a
) que variable r
( 'b
). Sin embargo, cuando x
tiene un tiempo de vida más corto, el verificador de préstamos rechazaría el programa:
fn main () { let r ; // ------------------+- Tiempo de vida 'a // | { // | let x = 5 ; // -+-- Tiempo de vida 'b | r = & x ; // | | // ERROR aquí: x no vive lo suficiente } // -| | // | println! ( "r: {}" , r ); // | } // ------------------+
Dado que el tiempo de vida de la variable referenciada ( 'b
) es más corto que el tiempo de vida de la variable que contiene la referencia ( 'a
), el verificador de préstamos genera errores, lo que impide x
que se utilice desde fuera de su alcance. [95]
Los tiempos de vida se pueden indicar utilizando parámetros de tiempo de vida explícitos en los argumentos de la función. Por ejemplo, el código siguiente especifica que la referencia devuelta por la función tiene el mismo tiempo de vida que original
(y no necesariamente el mismo tiempo de vida que prefix
): [96]
fn remove_prefix <' a > ( mut original : & ' a str , prefijo : & str ) -> & ' a str { si original . starts_with ( prefijo ) { original = original [ prefijo . len () .. ]; } original }
Cuando los tipos definidos por el usuario contienen referencias a datos, también necesitan utilizar parámetros de duración de vida. El ejemplo siguiente analiza algunas opciones de configuración de una cadena y crea una estructura que contiene las opciones. La función parse_config
también muestra la elisión de duración de vida, lo que reduce la necesidad de definir explícitamente los parámetros de duración de vida. [97]
utilizar std :: colecciones :: HashMap ; // Esta estructura tiene un parámetro de duración, 'src'. El nombre solo se utiliza dentro de la definición de la estructura. #[derive(Debug)] struct Config <' src > { hostname : & ' src str , username : & ' src str , } // El parámetro '_ lifetime, en este caso, se refiere al tiempo de vida anónimo adjunto al tipo // del argumento `config`. fn parse_config ( config : & str ) -> Config <' _ > { let key_values : HashMap < _ , _ > = config . lines () . filter ( | line | ! line . starts_with ( '#' )) . filter_map ( | line | line . split_once ( '=' )) . map ( | ( key , value ) | ( key . trim (), value . trim ()))) . collect (); Config { hostname : key_values [ "hostname" ], username : key_values [ "username" ], } } fn main () { let config = parse_config ( r#"nombre_host = foobar nombre_usuario=barfoo"# , ); println! ( "Configuración analizada: {:#?}" , config ); }
En el compilador, la propiedad y la duración trabajan juntas para evitar problemas de seguridad de la memoria, como punteros colgantes. [98]
Las características más avanzadas de Rust incluyen el uso de funciones genéricas . A una función genérica se le asignan parámetros genéricos , que permiten que la misma función se aplique a diferentes tipos de variables. Esta capacidad reduce el código duplicado [99] y se conoce como polimorfismo paramétrico .
El siguiente programa calcula la suma de dos cosas, para lo cual la adición se implementa utilizando una función genérica:
utilizar std :: ops :: Add ; // suma es una función genérica con un parámetro de tipo, T fn suma < T > ( num1 : T , num2 : T ) -> T donde T : Agregar < Salida = T > , // T debe implementar el rasgo Agregar donde la adición devuelve otro T { num1 + num2 // num1 + num2 es una sintaxis simplificada para num1.add(num2) proporcionada por el rasgo Agregar } fn main () { let resultado1 = suma ( 10 , 20 ); println! ( "La suma es: {}" , resultado1 ); // La suma es: 30 deje que result2 = suma ( 10.23 , 20.45 ); println! ( "La suma es: {}" , result2 ); // La suma es: 30.68 }
En el momento de la compilación, sum
se crean instancias de funciones polimórficas como con los tipos específicos que requiere el código; en este caso, suma de números enteros y suma de números flotantes.
Los genéricos se pueden utilizar en funciones para permitir la implementación de un comportamiento para diferentes tipos sin repetir el mismo código. Las funciones genéricas se pueden escribir en relación con otros genéricos, sin conocer el tipo real. [100]
El sistema de tipos de Rust admite un mecanismo llamado rasgos, inspirado en las clases de tipos del lenguaje Haskell , [6] para definir un comportamiento compartido entre diferentes tipos. Por ejemplo, el Add
rasgo se puede implementar para números flotantes y enteros, que se pueden sumar; y el rasgo Display
o Debug
se puede implementar para cualquier tipo que se pueda convertir en una cadena. Los rasgos se pueden usar para proporcionar un conjunto de comportamiento común para diferentes tipos sin conocer el tipo real. Esta función se conoce como polimorfismo ad hoc .
Las funciones genéricas pueden restringir el tipo genérico para implementar una característica o características particulares; por ejemplo, una add_one
función puede requerir que el tipo implemente Add
. Esto significa que una función genérica puede ser verificada por tipo tan pronto como se define. La implementación de genéricos es similar a la implementación típica de plantillas de C++: se genera una copia separada del código para cada instanciación. Esto se llama monomorfización y contrasta con el esquema de borrado de tipo que se usa típicamente en Java y Haskell. El borrado de tipo también está disponible a través de la palabra clave dyn
(abreviatura de dinámico). [101] Debido a que la monomorfización duplica el código para cada tipo usado, puede resultar en un código más optimizado para casos de uso específicos, pero el tiempo de compilación y el tamaño del binario de salida también aumentan. [102]
Además de definir métodos para un tipo definido por el usuario, la impl
palabra clave se puede utilizar para implementar un rasgo para un tipo. [64] Los rasgos pueden proporcionar métodos derivados adicionales cuando se implementan. [103] Por ejemplo, el rasgo Iterator
requiere que el next
método se defina para el tipo. Una vez que next
se define el método, el rasgo puede proporcionar métodos auxiliares funcionales comunes sobre el iterador, como map
o filter
. [104]
Los rasgos de Rust se implementan utilizando un envío estático , lo que significa que el tipo de todos los valores se conoce en el momento de la compilación; sin embargo, Rust también utiliza una característica conocida como objetos de rasgo para lograr el envío dinámico (también conocido como tipado de pato ). [105] Los objetos de rasgo enviados dinámicamente se declaran utilizando la sintaxis dyn Tr
donde Tr
es un rasgo. Los objetos de rasgo tienen un tamaño dinámico, por lo tanto, deben colocarse detrás de un puntero, como Box
. [106] El siguiente ejemplo crea una lista de objetos donde cada objeto se puede imprimir utilizando el Display
rasgo:
utilizar std :: fmt :: Mostrar ; sea v : Vec < Caja < dyn Pantalla >> = vec! [ Caja :: new ( 3 ), Caja :: new ( 5.0 ), Caja :: new ( "hola" ), ]; para x en v { println! ( "{x}" ); }
Si un elemento de la lista no implementa la Display
característica, provocará un error en tiempo de compilación. [107]
Rust está diseñado para ser seguro para la memoria . No permite punteros nulos, punteros colgantes o carreras de datos . [108] [109] [110] Los valores de datos se pueden inicializar solo a través de un conjunto fijo de formularios, todos los cuales requieren que sus entradas ya estén inicializadas. [111]
El código inseguro puede subvertir algunas de estas restricciones, utilizando la unsafe
palabra clave. [83] El código inseguro también puede usarse para funcionalidad de bajo nivel, como acceso a memoria volátil , intrínsecos específicos de la arquitectura, juegos de palabras de tipos y ensamblaje en línea. [112]
Rust no utiliza recolección de basura . La memoria y otros recursos se gestionan mediante la convención "la adquisición de recursos es inicialización", [113] con conteo de referencias opcional . Rust proporciona una gestión determinista de los recursos, con una sobrecarga muy baja . [114] Los valores se asignan en la pila de forma predeterminada, y todas las asignaciones dinámicas deben ser explícitas. [115]
Los tipos de referencia integrados que utilizan el &
símbolo no implican un recuento de referencias en tiempo de ejecución. La seguridad y validez de los punteros subyacentes se verifica en tiempo de compilación, lo que evita punteros colgantes y otras formas de comportamiento indefinido . [116] El sistema de tipos de Rust separa las referencias compartidas e inmutables de la forma &T
de las referencias únicas y mutables de la forma &mut T
. Una referencia mutable se puede convertir en una referencia inmutable, pero no al revés. [117]
Es posible ampliar el lenguaje Rust usando macros.
Una macro declarativa (también llamada "macro por ejemplo") es una macro, definida usando la macro_rules!
palabra clave, que utiliza la coincidencia de patrones para determinar su expansión. [118] [119] Un ejemplo es la println!()
macro. [120]
Las macros procedimentales son funciones de Rust que ejecutan y modifican el flujo de tokens de entrada del compilador antes de que se compilen otros componentes. Por lo general, son más flexibles que las macros declarativas, pero son más difíciles de mantener debido a su complejidad. [121] [122]
Las macros de procedimiento vienen en tres versiones:
custom!(...)
#[derive(CustomDerive)]
#[custom_attribute]
La rsx!
macro en el marco de interfaz Dioxus es un ejemplo de una macro similar a una función. [123] [124] La serde_derive
macro [125] proporciona una biblioteca de uso común para generar código para leer y escribir datos en muchos formatos , como JSON . Las macros de atributos se usan comúnmente para enlaces de lenguaje, como la extendr
biblioteca para enlaces de Rust a R. [126]
El siguiente código muestra el uso de las macros de procedimiento derivadas de Serialize
, Deserialize
, y Debug
para implementar la lectura y escritura de JSON, así como la capacidad de formatear una estructura para la depuración.
utilizar serde :: { Serializar , Deserializar }; #[derive(Serializar, Deserializar, Depurar)] struct Point { x : i32 , y : i32 , } fn main () { let punto = Punto { x : 1 , y : 2 }; deje que serializado = serde_json :: to_string ( & point ) .unwrap (); println! ( "serializado = {}" , serializado ); deje que deserialice : Punto = serde_json :: from_str ( & serializado ) .unwrap (); println! ( "deserializado = {:?}" , deserializado ); }
Rust no admite argumentos variádicos en funciones. En su lugar, utiliza macros . [127]
macro_rules! calculate { // El patrón para un solo `eval` ( eval $e : expr ) => {{ { let val : usize = $e ; // Fuerza a que los tipos sean enteros println! ( "{} = {}" , stringify! { $e }, val ); } }}; // Descomponer múltiples `eval`s recursivamente ( eval $e : expr , $( eval $es : expr ), + ) => {{ calculate ! { eval $e } calculate ! { $( eval $es ), + } }}; } fn main () { calcular ! { // Mira ma! Variadic `calculate!`! eval 1 + 2 , eval 3 + 4 , eval ( 2 * 3 ) + 1 } }
c_variadic
conmutador de características. Al igual que con otras interfaces de C, el sistema se considera unsafe
parte de Rust. [128]Rust tiene una interfaz de función externa (FFI) que se puede usar tanto para llamar a código escrito en lenguajes como C desde Rust como para llamar a código Rust desde esos lenguajes. A partir de 2024 [actualizar], existe una biblioteca externa llamada CXX para llamar a o desde C++. [129] Rust y C difieren en cómo disponen las estructuras en la memoria, por lo que a las estructuras de Rust se les puede dar un #[repr(C)]
atributo, lo que obliga a tener el mismo diseño que la estructura C equivalente. [130]
El ecosistema Rust incluye su compilador, su biblioteca estándar y componentes adicionales para el desarrollo de software. La instalación de componentes normalmente la gestiona Rust rustup
, un instalador de la cadena de herramientas de Rust desarrollado por el proyecto Rust. [131]
El compilador de Rust, , traduce el código de Rust en un IRrustc
LLVM de bajo nivel . Luego, se invoca LLVM como un subcomponente para aplicar optimizaciones y traducir el IR resultante en código de objeto . Luego, se utiliza un enlazador para combinar los objetos en una única imagen ejecutable o archivo binario. [132]
Además de LLVM, el compilador también admite el uso de backends alternativos como GCC y Cranelift para la generación de código. [133] La intención de esos backends alternativos es aumentar la cobertura de la plataforma de Rust o mejorar los tiempos de compilación. [134] [135]
La biblioteca estándar de Rust define e implementa muchos tipos de datos personalizados ampliamente utilizados, incluidas las estructuras de datos centrales como Vec
, Option
, y HashMap
, así como los tipos de puntero inteligente . Rust también proporciona una forma de excluir la mayor parte de la biblioteca estándar utilizando el atributo #![no_std]
; esto permite que las aplicaciones, como los dispositivos integrados, eliminen el código de dependencia o proporcionen sus propias estructuras de datos centrales. Internamente, la biblioteca estándar se divide en tres partes, core
, alloc
, y std
, donde std
y alloc
se excluyen mediante #![no_std]
. [136]
Cargo es el sistema de compilación y el administrador de paquetes de Rust . Descarga, compila, distribuye y carga paquetes (llamados crates ) que se mantienen en un registro oficial. También actúa como interfaz para Clippy y otros componentes de Rust. [137]
De manera predeterminada, Cargo obtiene sus dependencias del registro contribuido por el usuario crates.io , pero los repositorios de Git y los cajones en el sistema de archivos local y otras fuentes externas también se pueden especificar como dependencias. [138]
Rustfmt es un formateador de código para Rust. Formatea espacios en blanco y sangrías para producir código de acuerdo con un estilo común , a menos que se especifique lo contrario. Se puede invocar como un programa independiente o desde un proyecto Rust a través de Cargo. [139]
Clippy es la herramienta de análisis de errores integrada de Rust para mejorar la corrección, el rendimiento y la legibilidad del código de Rust. A partir de 2024 [actualizar], tiene más de 700 reglas. [140] [141]
Después de Rust 1.0, se desarrollan nuevas características en versiones nocturnas que se lanzan diariamente. Durante cada ciclo de lanzamiento de seis semanas, los cambios de las versiones nocturnas se lanzan a la versión beta, mientras que los cambios de la versión beta anterior se lanzan a una nueva versión estable. [142]
Cada dos o tres años se produce una nueva "edición". Las ediciones se lanzan para permitir la realización de cambios limitados , como la promoción await
a una palabra clave para admitir funciones async/await . Los crates que apuntan a diferentes ediciones pueden interoperar entre sí, por lo que un crate puede actualizarse a una nueva edición incluso si sus llamadores o sus dependencias aún apuntan a ediciones anteriores. La migración a una nueva edición puede ser asistida con herramientas automatizadas. [143]
rust-analyzer es una colección de utilidades que proporciona a los entornos de desarrollo integrados (IDE) y editores de texto información sobre un proyecto Rust a través del Protocolo de servidor de lenguaje . Esto permite funciones como el autocompletado y la visualización de errores de compilación durante la edición. [144]
En general, las garantías de seguridad de memoria de Rust no imponen una sobrecarga en tiempo de ejecución. [145] Una excepción notable es la indexación de matrices , que se verifica en tiempo de ejecución, aunque esto a menudo no afecta el rendimiento. [146] Dado que no realiza la recolección de basura, Rust suele ser más rápido que otros lenguajes seguros para la memoria. [147] [88] [148]
Rust ofrece dos "modos": seguro e inseguro. El modo seguro es el "normal", en el que se escribe la mayor parte de Rust. En el modo inseguro, el desarrollador es responsable de la seguridad de la memoria del código, que es utilizado por los desarrolladores en casos en los que el compilador es demasiado restrictivo. [149]
Muchas de las características de Rust son las llamadas abstracciones de costo cero , lo que significa que se optimizan en tiempo de compilación y no incurren en penalizaciones de tiempo de ejecución. [150] El sistema de propiedad y préstamo permite implementaciones de copia cero para algunas tareas sensibles al rendimiento, como el análisis . [151] El envío estático se utiliza de forma predeterminada para eliminar las llamadas a métodos , con la excepción de los métodos llamados en objetos de rasgos dinámicos. [152] El compilador también utiliza la expansión en línea para eliminar las llamadas a funciones y las invocaciones de métodos enviados estáticamente. [153]
Dado que Rust utiliza LLVM , cualquier mejora de rendimiento en LLVM también se traslada a Rust. [154] A diferencia de C y C++, Rust permite reordenar elementos de estructura y enumeración [155] para reducir los tamaños de las estructuras en la memoria, para una mejor alineación de la memoria y para mejorar la eficiencia del acceso a la memoria caché . [156]
Rust se ha utilizado en software en diferentes dominios. Rust fue financiado inicialmente por Mozilla como parte del desarrollo de Servo , un motor de navegador paralelo experimental, en colaboración con Samsung . [157] Los componentes del motor Servo se incorporaron más tarde al motor de navegador Gecko subyacente a Firefox. [158] En enero de 2023, Google ( Alphabet ) anunció el soporte para el uso de bibliotecas de terceros de Rust en Chromium . [159] [160]
Rust se utiliza en varios proyectos de software backend de grandes servicios web . OpenDNS , un servicio de resolución de DNS propiedad de Cisco , utiliza Rust internamente. [161] [162] Amazon Web Services utiliza Rust en "componentes sensibles al rendimiento" de sus varios servicios. En 2019, AWS publicó el código abierto de Firecracker , una solución de virtualización escrita principalmente en Rust. [163] Microsoft Azure IoT Edge, una plataforma utilizada para ejecutar servicios de Azure en dispositivos IoT , tiene componentes implementados en Rust. [164] Microsoft también utiliza Rust para ejecutar módulos en contenedores con WebAssembly y Kubernetes . [165] Cloudflare , una empresa que proporciona servicios de red de distribución de contenido , utilizó Rust para crear un nuevo proxy web llamado Pingora para aumentar el rendimiento y la eficiencia. [166] El administrador de paquetes npm comenzó a utilizar Rust para su servicio de autenticación de producción en 2019. [167] [168] [169]
En sistemas operativos, los desarrolladores de Android estaban usando Rust en 2021 para reescribir componentes existentes. [170] [171] El proyecto Rust para Linux , lanzado en 2020, agregó soporte inicial de Rust a Linux a fines de 2022, y los primeros controladores de Linux escritos en Rust se lanzaron a fines de 2023. [172] [173] Microsoft está reescribiendo partes de Windows en Rust. [174] El proyecto r9 tiene como objetivo volver a implementar Plan 9 de Bell Labs en Rust. [175] Rust se ha utilizado en el desarrollo de nuevos sistemas operativos como Redox , un sistema operativo "similar a Unix" y microkernel , [176] Theseus, un sistema operativo experimental con gestión de estado modular, [177] [178] y la mayor parte de Fuchsia . [179] Rust también se utiliza para herramientas de línea de comandos y componentes del sistema operativo, incluido stratisd , un administrador de sistema de archivos [180] [181] y COSMIC, un entorno de escritorio de System76 . [182]
En el desarrollo web, Deno , un entorno de ejecución seguro para JavaScript y TypeScript , está construido sobre V8 usando Rust y Tokio. [183] Otras adopciones notables en este espacio incluyen Ruffle , un emulador SWF de código abierto , [184] y Polkadot , una plataforma de cadena de bloques y criptomonedas de código abierto . [185]
Discord , una empresa de software de mensajería instantánea , ha reescrito partes de su sistema en Rust para aumentar el rendimiento en 2020. Ese mismo año, Dropbox anunció que su sincronización de archivos había sido reescrita en Rust. Facebook ( Meta ) también ha utilizado Rust para rediseñar su sistema que administra el código fuente de proyectos internos. [16]
En la encuesta de desarrolladores de Stack Overflow de 2023 , el 13 % de los encuestados había realizado recientemente un desarrollo extenso en Rust. [186] La encuesta también nombró a Rust como el "lenguaje de programación más amado" todos los años desde 2016 hasta 2023 (inclusive) y el "lenguaje de programación más admirado" en 2024, según la cantidad de desarrolladores interesados en seguir trabajando en el mismo lenguaje. [187] [nota 7] En 2023, Rust fue la sexta "tecnología más buscada", con un 31 % de desarrolladores que actualmente no trabajan en Rust expresando interés en hacerlo. [186]
Rust ha sido estudiado en la investigación académica, tanto por las propiedades del lenguaje en sí como por la utilidad que proporciona para escribir software utilizado en la investigación. Se han examinado sus características en cuanto a seguridad [188] [149] y rendimiento [189] .
En un artículo de revista publicado en Proceedings of the International Astronomical Union , los astrofísicos Blanco-Cuaresma y Bolmont reimplementaron programas responsables de simular sistemas multiplanetarios en Rust, y descubrieron que era un lenguaje de programación competitivo por su "velocidad y precisión". [14] Asimismo, un artículo publicado en Nature compartió varias historias de bioinformáticos que usan Rust por su rendimiento y seguridad. [137] Sin embargo, ambos artículos han citado los conceptos únicos de Rust, incluido su sistema de propiedad, que es difícil de aprender como uno de los principales inconvenientes para adoptar Rust.
Rust se ha destacado por tener una comunidad inclusiva, y particularmente por dar la bienvenida a personas de la comunidad queer , en parte debido a su código de conducta que describía un conjunto de expectativas que los miembros de la comunidad Rust debían seguir. Un artículo de MIT Technology Review describió a la comunidad Rust como "inusualmente amigable" con los recién llegados. [16] [137]
La Fundación Rust es una organización de membresía sin fines de lucro incorporada en Estados Unidos , con los propósitos principales de respaldar el proyecto técnico como entidad legal y ayudar a administrar la marca registrada y los activos de infraestructura. [192] [39]
Se estableció el 8 de febrero de 2021, con cinco miembros corporativos fundadores (Amazon Web Services, Huawei, Google, Microsoft y Mozilla). [193] La junta directiva de la fundación está presidida por Shane Miller. [194] A partir de fines de 2021, su directora ejecutiva y directora ejecutiva es Rebecca Rumbul. [195] Antes de esto, Ashley Williams fue directora ejecutiva interina. [39]
El proyecto Rust está compuesto por equipos que son responsables de diferentes subáreas del desarrollo. El equipo de compiladores desarrolla, administra y optimiza los componentes internos del compilador; y el equipo de lenguaje diseña nuevas características del lenguaje y ayuda a implementarlas. El sitio web del proyecto Rust enumera 6 equipos de alto nivel a partir de julio de 2024. [actualizar][ 196] Los representantes de los equipos forman el consejo de liderazgo, que supervisa el proyecto Rust en su conjunto. [197]
str
y String
son siempre cadenas UTF-8 válidas y pueden contener ceros internos.Quienes estén familiarizados con el estilo Elm pueden reconocer que los mensajes
--explain
actualizados se inspiran en gran medida en el enfoque Elm.
Están inspirados en... tipos de propiedad y punteros prestados en el lenguaje de programación Rust.
Ambos son lenguajes con llaves, con una sintaxis similar a C que los hace poco intimidantes para los programadores de C.
Observamos una gran variación en los costos generales de la indexación controlada: el 23,6% de los puntos de referencia informan impactos significativos en el rendimiento de la indexación controlada, pero el 64,5% informa poco o ningún impacto y, sorprendentemente, el 11,8% informa una mejora en el rendimiento ... En última instancia, si bien la indexación sin control puede mejorar el rendimiento, la mayoría de las veces no lo hace.
... Si bien algunos compiladores (por ejemplo, Rust) admiten la reordenación de estructuras [82], los compiladores de C y C++ tienen prohibido reordenar las estructuras de datos (por ejemplo, struct o class) [74] ...