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 vida útil del objeto 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 parte del empleado de Mozilla Research Graydon Hoare, llamado así por el grupo de hongos que están "sobrediseñados para sobrevivir". Mozilla comenzó a patrocinar el proyecto en 2009 y emplearía a una docena de ingenieros para trabajar en él a tiempo completo durante los siguientes diez años. [16]
Alrededor de 2010, el trabajo pasó del compilador inicial escrito en OCaml a un compilador autoalojado basado en LLVM escrito en Rust. El nuevo compilador de Rust se compiló con éxito en 2011. [17] [ Se necesita una mejor fuente ] En el otoño de 2011, se desarrolló el logotipo de Rust basado en un plato de bicicleta . [18]
El sistema de tipos de Rust sufrió cambios significativos entre las versiones 0.2, 0.3 y 0.4. En la versión 0.2, que se lanzó en marzo de 2012, se introdujeron las clases por primera vez. [19] Cuatro meses después, la versión 0.3 agregó destructores y polimorfismo , mediante el uso de interfaces. [20] En octubre de 2012, se lanzó la versión 0.4, que agregó rasgos como un medio de herencia . Las interfaces se combinaron con rasgos y se eliminaron como una característica separada; y las clases se reemplazaron por una combinación de implementaciones y tipos estructurados . [21]
A principios de la década de 2010, la gestión de memoria a través del sistema de propiedad se consolidó gradualmente para evitar errores de memoria. En 2013, se eliminó el recolector de basura de Rust , pero se mantuvieron las reglas de propiedad. [16]
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 desaceleró porque cambió radicalmente de una versión a otra. [22] La primera versión estable , Rust 1.0, se lanzó el 15 de mayo de 2015. [16]
El desarrollo del motor de navegación Servo continuó junto con el crecimiento de Rust. En septiembre de 2017, se lanzó Firefox 57 como la primera versión que incorporó componentes de Servo, en un proyecto llamado " Firefox Quantum ". [23]
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 . [24] [25] El equipo detrás de Servo se disolvió. El evento generó inquietudes sobre el futuro de Rust, ya que algunos miembros del equipo eran contribuyentes activos de Rust. [26] 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. [27]
El 8 de febrero de 2021, las cinco empresas fundadoras ( AWS , Huawei , Google , Microsoft y Mozilla ) anunciaron la formación de la Fundación Rust . [28] [29] 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++. [30]
El 22 de noviembre de 2021, el Equipo de Moderación, responsable de hacer cumplir las normas de la comunidad y el Código de Conducta, anunció su renuncia "en protesta porque el Equipo Central se ha puesto a sí mismo al margen de cualquier responsabilidad". [31] 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. [32]
El 6 de abril de 2023, la Fundación Rust publicó un borrador de una nueva política de marca registrada , revisando sus reglas sobre cómo se pueden usar el logotipo y el nombre de Rust, lo que resultó en reacciones negativas de los usuarios y colaboradores de Rust. [33]
La sintaxis de Rust es similar a la de C y C++, [34] [35] aunque muchas de sus características fueron influenciadas por lenguajes de programación funcional como OCaml . [36] Hoare describió 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 . [17] 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 . [37] 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. [38] 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. [39] El siguiente ejemplo utiliza //
, que denota el comienzo de un comentario . [40]
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. [41] 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 . [42]
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. [43]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. [44]
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. [45] 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. [46]'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 es menor 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. [47] 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 , [48] 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: [49]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. [50]
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. [51]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. [52] Los literales de byte como están disponibles para representar el valor ASCII (en ) de un carácter específico. [53]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 . [54] 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 . [55]
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. [56] 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. [57] Tanto las estructuras como las enumeraciones pueden contener campos con diferentes tipos. [58] Se pueden definir nombres alternativos para el mismo tipo con la type
palabra clave. [59]
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. [60]
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): [76]
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. [77] 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). [78]&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. [79]
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. [80]
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. [81] 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. [82] Con estas reglas, Rust puede evitar la creación y el uso de punteros colgantes : [82] [83]
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. [84]
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. [85]
Los tiempos de vida suelen ser una parte implícita de todos los tipos de referencia en Rust. Cada tiempo de vida abarca un conjunto de ubicaciones en el código para las que una variable es válida. Por ejemplo, una referencia a una variable local tiene un tiempo de vida correspondiente al bloque en el que está definida: [86]
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 Rust utiliza duraciones de vida para garantizar que los valores a los que apunta una referencia sigan siendo válidos. [87] [88] En el ejemplo anterior, almacenar una referencia a una variable x
en r
es válido, ya que variable x
tiene una duración de vida más larga ( 'a
) que variable r
( 'b
). Sin embargo, cuando x
tiene una duración de vida más corta, 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 ; // | | } // -| | // | 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. [89]
Los parámetros de duración de vida hacen explícita la duración de las referencias, por ejemplo, especificando la fuente de una salida: [90]
fn remove_prefix <' a > ( mut original : & ' a str , prefijo : & str ) -> & ' a str { si original . starts_with ( prefijo ) { original = original [ prefijo . len () .. ]; } original }
Con información sobre cómo se relacionan los tiempos de vida del valor de salida de la función con sus entradas, el compilador puede evitar problemas de seguridad de memoria, como punteros colgantes.
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. [91]
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 ); }
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 [92] 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 resultado2 = suma ( 10.23 , 20.45 ); println! ( "La suma es: {}" , resultado2 ); // 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. [93]
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). [94] 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. [95]
Además de definir métodos para un tipo definido por el usuario, la impl
palabra clave se puede utilizar para implementar un atributo para un tipo. [60] Los atributos pueden proporcionar métodos derivados adicionales cuando se implementan. [96] Por ejemplo, el atributo Iterator
requiere que el next
método se defina para el tipo. Una vez que next
se define el método, el atributo puede proporcionar métodos auxiliares funcionales comunes sobre el iterador, como map
o filter
. [97]
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 ). [98] 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
. [99] 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. [100]
Rust está diseñado para ser seguro para la memoria . No permite punteros nulos, punteros colgantes o carreras de datos . [101] [102] [103] 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. [104]
El código inseguro puede subvertir algunas de estas restricciones, utilizando la unsafe
palabra clave. [79] 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. [105]
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", [106] con conteo de referencias opcional . Rust proporciona una gestión determinista de los recursos, con una sobrecarga muy baja . [107] Los valores se asignan en la pila de forma predeterminada, y todas las asignaciones dinámicas deben ser explícitas. [108]
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 . [109] 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. [110]
En Rust, las funciones anónimas se denominan cierres. [111] Se definen utilizando la siguiente sintaxis:
|< parámetro - nombre > : < tipo >| -> < retorno - tipo > { < cuerpo > };
Por ejemplo:
sea f = | x : i32 | -> i32 { x * 2 };
Sin embargo, con la inferencia de tipos, el compilador puede inferir el tipo de cada parámetro y el tipo de retorno, por lo que la forma anterior se puede escribir como:
sea f = | x | { x * 2 };
Con cierres con una sola expresión (es decir, un cuerpo con una línea) y un tipo de retorno implícito, se pueden omitir las llaves:
sea f = | x | x * 2 ;
Los cierres sin parámetros de entrada se escriben así:
sea f = || println! ( "¡Hola, mundo!" );
Los cierres se pueden pasar como parámetros de entrada de funciones que esperan un puntero de función:
// Una función que toma un puntero de función como argumento y lo llama con // el valor `5`. fn apply ( f : fn ( i32 ) -> i32 ) -> i32 { // Sin punto y coma, para indicar un retorno implícito f ( 5 ) } fn main () { // Definiendo el cierre let f = | x | x * 2 ; println! ( "{}" , aplicar ( f )); // 10 println! ( "{}" , f ( 5 )); // 10 }
Sin embargo, es posible que se necesiten reglas complejas para describir cómo se capturan los valores en el cuerpo del cierre. Se implementan utilizando los rasgos , y : Fn
[ FnMut
112 ]FnOnce
Fn
: el cierre captura por referencia ( &T
). Se utilizan para funciones que aún pueden ser llamadas si solo tienen acceso de referencia (con &
) a su entorno.FnMut
: el cierre captura por referencia mutable ( &mut T
). Se utilizan para funciones que pueden ser llamadas si tienen acceso de referencia mutable (con &mut
) a su entorno.FnOnce
: el cierre captura por valor ( T
). Se utilizan para funciones que solo se llaman una vez.Con estas características, el compilador capturará las variables de la manera menos restrictiva posible. [112] Ayudan a gobernar cómo se mueven los valores entre ámbitos, lo que es muy importante ya que Rust sigue una construcción de por vida para garantizar que los valores se "tomen prestados" y se muevan de una manera predecible y explícita. [113]
A continuación se muestra cómo se puede pasar un cierre como parámetro de entrada utilizando el Fn
rasgo:
// Una función que toma un valor de tipo F (que se define como // un tipo genérico que implementa el rasgo `Fn`, por ejemplo, un cierre) // y lo llama con el valor `5`. fn apply_by_ref < F > ( f : F ) -> i32 donde F : Fn ( i32 ) -> i32 { f ( 5 ) } fn main () { let f = | x | { println! ( "Obtuve el valor: {}" , x ); x * 2 }; // Aplica la función antes de imprimir su valor de retorno println! ( "5 * 2 = {}" , apply_by_ref ( f )); } // ~~ Salida del programa ~~ // Obtuve el valor: 5 // 5 * 2 = 10
La definición de función anterior también se puede abreviar para mayor comodidad de la siguiente manera:
fn aplicar_por_ref ( f : impl Fn ( i32 ) -> i32 ) -> i32 { f ( 5 ) }
Es posible ampliar el lenguaje Rust usando macros.
Una macro declarativa (también llamada "macro por ejemplo") es una macro que utiliza la coincidencia de patrones para determinar su expansión. [114] [115]
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. [116] [117]
Las macros de procedimiento vienen en tres versiones:
custom!(...)
#[derive(CustomDerive)]
#[custom_attribute]
La println!
macro es un ejemplo de una macro similar a una función. La serde_derive
macro [118] 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 utilizan comúnmente para enlaces de lenguaje, como la extendr
biblioteca para enlaces de Rust a R. [ 119]
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 . [120]
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. [121]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 [update], existe una biblioteca externa llamada CXX para llamar a o desde C++. [122] 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. [123]
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. [124]
El compilador de Rust se llama rustc
, y traduce el código de Rust a un lenguaje de bajo nivel llamado representación intermedia LLVM (LLVM IR). Luego, LLVM se invoca como un subcomponente para traducir el código IR a código de máquina . Luego, se utiliza un enlazador para combinar múltiples paquetes juntos como un solo archivo ejecutable o binario. [125] [126]
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. [127] La intención de esos backends alternativos es aumentar la cobertura de la plataforma de Rust o mejorar los tiempos de compilación. [128] [129]
The Rust standard library defines and implements many widely used custom data types, including core data structures such as Vec
, Option
, and HashMap
, as well as smart pointer types. Rust also provides a way to exclude most of the standard library using the attribute #![no_std]
; this enables applications, such as embedded devices, which want to remove dependency code or provide their own core data structures. Internally, the standard library is divided into three parts, core
, alloc
, and std
, where std
and alloc
are excluded by #![no_std]
.[130]
Cargo is Rust's build system and package manager. It downloads, compiles, distributes, and uploads packages—called crates—that are maintained in an official registry. It also acts as a front-end for Clippy and other Rust components.[131]
By default, Cargo sources its dependencies from the user-contributed registry crates.io, but Git repositories and crates in the local filesystem, and other external sources can also be specified as dependencies.[132]
Rustfmt is a code formatter for Rust. It formats whitespace and indentation to produce code in accordance with a common style, unless otherwise specified. It can be invoked as a standalone program, or from a Rust project through Cargo.[133]
Clippy is Rust's built-in linting tool to improve the correctness, performance, and readability of Rust code. As of 2024[update], it has more than 700 rules.[134][135]
Following Rust 1.0, new features are developed in nightly versions which are released daily. During each six-week release cycle, changes to nightly versions are released to beta, while changes from the previous beta version are released to a new stable version.[136]
Every two or three years, a new "edition" is produced. Editions are released to allow making limited breaking changes, such as promoting await
to a keyword to support async/await features. Crates targeting different editions can interoperate with each other, so a crate can upgrade to a new edition even if its callers or its dependencies still target older editions. Migration to a new edition can be assisted with automated tooling.[137]
rust-analyzer is a collection of utilities that provides Integrated development environments (IDEs) and text editors with information about a Rust project through the Language Server Protocol. This enables features including autocompletion, and the display of compilation errors while editing.[138]
In general, Rust's memory safety guarantees do not impose a runtime overhead.[139] A notable exception is array indexing which is checked at runtime, though this often does not impact performance.[140] Since it does not perform garbage collection, Rust is often faster than other memory-safe languages.[141][84][142]
Rust provides two "modes": safe and unsafe. Safe mode is the "normal" one, in which most Rust is written. In unsafe mode, the developer is responsible for the code's memory safety, which is used by developers for cases where the compiler is too restrictive.[143]
Many of Rust's features are so-called zero-cost abstractions, meaning they are optimized away at compile time and incur no runtime penalty.[144] The ownership and borrowing system permits zero-copy implementations for some performance-sensitive tasks, such as parsing.[145] Static dispatch is used by default to eliminate method calls, with the exception of methods called on dynamic trait objects.[146] The compiler also uses inline expansion to eliminate function calls and statically-dispatched method invocations.[147]
Since Rust utilizes LLVM, any performance improvements in LLVM also carry over to Rust.[148] Unlike C and C++, Rust allows for reordering struct and enum elements[149] to reduce the sizes of structures in memory, for better memory alignment, and to improve cache access efficiency.[150]
Rust has been used in software across different domains. Rust was initially funded by Mozilla as part of developing Servo, an experimental parallel browser engine, in collaboration with Samsung.[151] Components from the Servo engine were later incorporated in the Gecko browser engine underlying Firefox.[152] In January 2023, Google (Alphabet) announced support for using third party Rust libraries in Chromium.[153][154]
Rust is used in several backend software projects of large web services. OpenDNS, a DNS resolution service owned by Cisco, uses Rust internally.[155][156] Amazon Web Services uses Rust in "performance-sensitive components" of its several services. In 2019, AWS has open-sourced Firecracker, a virtualization solution primarily written in Rust.[157] Microsoft Azure IoT Edge, a platform used to run Azure services on IoT devices, has components implemented in Rust.[158] Microsoft also uses Rust to run containerized modules with WebAssembly and Kubernetes.[159] Cloudflare, a company providing content delivery network services, used Rust to build a new web proxy named Pingora for increased performance and efficiency.[160]The npm package manager started using Rust for its production authentication service in 2019.[161][162][163]
In operating systems, the Android developers were using Rust in 2021 to rewrite existing components.[164][165]The Rust for Linux project, launched in 2020, added initial Rust support to Linux in late 2022, and the first Linux drivers written in Rust were released in late 2023.[166][167]Microsoft is rewriting parts of Windows in Rust.[168] The r9 project aims to re-implement Plan 9 from Bell Labs in Rust.[169] Rust has been used in the development of new operating systems such as Redox, a "Unix-like" operating system and microkernel,[170] Theseus, an experimental operating system with modular state management,[171][172] and most of Fuchsia.[173] Rust is also used for command-line tools and operating system components, including stratisd, a file system manager[174][175] and COSMIC, a desktop environment by System76.[176]
In web development, Deno, a secure runtime for JavaScript and TypeScript, is built on top of V8 using Rust and Tokio.[177] Other notable adoptions in this space include Ruffle, an open-source SWF emulator,[178] and Polkadot, an open source blockchain and cryptocurrency platform.[179]
Discord, an instant messaging software company, has rewritten parts of its system in Rust for increased performance in 2020. In the same year, Dropbox announced that its file synchronization had been rewritten in Rust. Facebook (Meta) has also used Rust to redesign its system that manages source code for internal projects.[16]
In the 2023 Stack Overflow Developer Survey, 13% of respondents had recently done extensive development in Rust.[180] The survey also named Rust the "most loved programming language" every year from 2016 to 2023 (inclusive), based on the number of developers interested in continuing to work in the same language.[181][note 5] In 2023, Rust was the 6th "most wanted technology", with 31% of developers not currently working in Rust expressing an interest in doing so.[180]
Rust has been studied in academic research, both for properties of the language itself as well as the utility the language provides for writing software used for research. Its features around safety[182][143] and performance[183] have been examined.
In a journal article published to Proceedings of the International Astronomical Union, astrophysicists Blanco-Cuaresma and Bolmont re-implemented programs responsible for simulating multi-planet systems in Rust, and found it to be a competitive programming language for its "speed and accuracy".[15] Likewise, an article published on Nature shared several stories of bioinformaticians using Rust for its performance and safety.[131] However, both articles have cited Rust's unique concepts, including its ownership system, being difficult to learn as one of the main drawbacks to adopting Rust.
Rust has been noted as having an inclusive community, and particularly welcomed people from the queer community, partly due to its code of conduct which outlined a set of expectations for Rust community members to follow. One MIT Technology Review article described the Rust community as "unusually friendly" to newcomers.[16][131]
The Rust Foundation is a non-profit membership organization incorporated in United States, with the primary purposes of backing the technical project as a legal entity and helping to manage the trademark and infrastructure assets.[186][35]
It was established on February 8, 2021, with five founding corporate members (Amazon Web Services, Huawei, Google, Microsoft, and Mozilla).[187] The foundation's board is chaired by Shane Miller.[188] Starting in late 2021, its Executive Director and CEO is Rebecca Rumbul.[189] Prior to this, Ashley Williams was interim executive director.[35]
The Rust project is composed of teams that are responsible for different subareas of the development. The compiler team develops, manages, and optimizes compiler internals; and the language team designs new language features and helps implement them. The Rust project website lists 6 top-level teams as of July 2024[update].[190] Representatives among teams form the Leadership council, which oversees the Rust project as a whole.[191]
str
and String
are always valid UTF-8 and can contain internal zeros.Those of you familiar with the Elm style may recognize that the updated --explain messages draw heavy inspiration from the Elm approach.
They are inspired by ... ownership types and borrowed pointers in the Rust programming language.
Both are curly bracket languages, with C-like syntax that makes them unintimidating for C programmers.
We observe a large variance in the overheads of checked indexing: 23.6% of benchmarks do report significant performance hits from checked indexing, but 64.5% report little-to-no impact and, surprisingly, 11.8% report improved performance ... Ultimately, while unchecked indexing can improve performance, most of the time it does not.
... While some compilers (e.g., Rust) support structure reordering [82], C & C++ compilers are forbidden to reorder data structures (e.g., struct or class) [74] ...