Rust ofrece a los programadores una forma de escribir software seguro para la memoria sin recolección de basura, funcionando a la velocidad nativa de la máquina. También es un lenguaje complejo de dominar, con una curva de aprendizaje inicial bastante pronunciada. Aquí hay cinco trampas, inconvenientes y trampas a las que debe prestar atención cuando se esté recuperando con Rust, y para los más experimentados. Óxido los desarrolladores también.
Problemas de Rust: 6 cosas que necesitas saber sobre cómo escribir código Rust
- No puedes «desactivar» el verificador de préstamos
- No uses ‘
_
‘para las variables que desea vincular - Los cierres no tienen las mismas reglas de vida que las funciones.
- Los destructores no siempre se ejecutan cuando expira un préstamo
- Tenga cuidado con las cosas inseguras y las vidas ilimitadas
.unwrap()
entrega el control de manejo de errores
No puedes «desactivar» el verificador de préstamos
La propiedad, los préstamos y la vida útil están integrados en Rust. Son una parte integral de cómo el lenguaje mantiene la seguridad de la memoria sin recolección de basura.
Algunos otros lenguajes ofrecen herramientas de verificación de código que alertan al desarrollador sobre problemas de seguridad o de memoria, pero aún permiten compilar el código. Rust no funciona de esa manera. El verificador de préstamo—la parte del compilador de Rust que verifica todo operaciones de propiedad son válidos: no es una utilidad opcional que se pueda desactivar. El código que no es válido para el verificador de préstamos no se compilará, punto.
Se podría (y tal vez debería escribirse) un artículo completo sobre cómo no luchar contra el verificador de préstamos. Vale la pena revisar Rust by Ejemplo sección sobre alcance para ver cómo funcionan las reglas para muchos comportamientos comunes.
En las primeras etapas de su viaje a Rust, recuerde que siempre puede solucionar los problemas de propiedad haciendo copias con .clone()
. Para partes del programa que no requieren un alto rendimiento, hacer copias rara vez tendrá un impacto mensurable. Luego, puedes concentrarte en las partes que hacer Necesita el máximo rendimiento sin copia y descubra cómo hacer que sus préstamos y su vida útil sean más eficientes en esas partes del programa.
No utilice ‘_’ para las variables que desee vincular
El nombre de la variable _
—un guión bajo—tiene un comportamiento especial en Rust. Significa que el valor que se recibe en la variable no está vinculado a ella. Normalmente se utiliza para recibir valores que deben descartarse inmediatamente. Si algo emite un must_use
advirtiendo, por ejemplo, asignándolo a _
es una forma típica de silenciar esa advertencia.
Con ese fin, no utilice el guión bajo para ningún valor que persista más allá de la declaración en la que se usa. Tenga en cuenta que aquí estamos hablando del declaraciónno la alcance.
Los escenarios a tener en cuenta son aquellos en los que desea que algo se mantenga hasta que quede fuera de alcance. Si tienes un bloque de código como
let _ = String::from(" Hello World ").trim();
la cadena creada inmediatamente quedará fuera del alcance después de esa declaración; no se llevará a cabo hasta el final del bloque. (La llamada al método es para garantizar que los resultados no se eliminen durante la compilación).
La manera fácil de evitar este problema es solo usa nombres como _user
o _item
para tareas que desea conservar hasta el final del alcance pero que no planea usar para mucho más.
Los cierres no tienen las mismas reglas de vida que las funciones.
Considere esta función:
fn function(x: &i32) -> &i32 {
x
}
Podrías intentar expresar esta función como un cierre, para un valor de retorno de una función:
fn main() {
let closure = |x: &i32| x;
}
El único problema es que no funciona. El compilador emitirá un graznido con el error: lifetime may not live long enough
porque la entrada y la salida del cierre tienen vidas útiles diferentes.
Una forma de solucionar esto sería utilizar una referencia estática:
fn main() {
let _closure: &dyn Fn(&i32) -> &i32 = &|x: &i32| x;
}
Usar una función separada es más detallado pero evita este tipo de problemas. Hace que los ámbitos sean más claros y fáciles de analizar visualmente.
Los destructores no siempre se ejecutan cuando expira un préstamo
Al igual que C++, Rust te permite crear destructores de tipos, que pueden ejecutarse cuando un objeto queda fuera de alcance. Pero eso no significa que sean garantizado correr.
Esto también es cierto, quizás doblemente, cuando expira un préstamo sobre un objeto determinado. Si un préstamo vence en algo, eso no implica que su destructor ya se haya ejecutado. De hecho, hay ocasiones en las que no desea que se ejecute el destructor sólo porque un préstamo ha caducado (por ejemplo, cuando mantiene un puntero a algo).
La documentación de Rust tiene Directrices para garantizar que se ejecute un destructor.y para saber cuándo se garantiza que se ejecutará un destructor.
Tenga cuidado con las cosas inseguras y las vidas ilimitadas
La palabra clave unsafe
existe para etiquetar código Rust que puede hacer cosas como eliminar referencias de punteros sin formato. Es el tipo de cosas que no necesitas hacer muy a menudo en Rust (¡esperamos!), pero cuando lo haces, ahora tienes un mundo completamente nuevo de problemas potenciales.
Por ejemplo, eliminar la referencia a un puntero sin formato producido por un unsafe
operación resulta en una vida ilimitada. Rustonomicón, el libro sobre el inseguro Rust, advierte que una vida ilimitada «se vuelve tan grande como lo exige el contexto». Eso significa que puede crecer inesperadamente más allá de lo que originalmente necesitaba o pretendía.
Si eres escrupuloso con lo que haces con una referencia ilimitada, no deberías tener ningún problema. Pero por seguridad, es mejor colocar punteros sin referencia en una función y usar tiempos de vida en el límite de la función, en lugar de dejarlos moverse dentro del alcance de una función.
.unwrap() entrega el control de manejo de errores
Siempre que una operación devuelve un Result
, hay dos formas básicas de manejarlo. uno esta con .unwrap()
o uno de sus primos (como .unwrap_or()
). El otro esta con una en toda regla match
declaración para manejar un Err
resultado.
.unwrap()
La gran ventaja de es que es conveniente. Si se encuentra en una ruta de código en la que nunca espera que surja una condición de error, o donde de todos modos sería imposible solucionar una condición de error, puede usar .unwrap()
para obtener el valor que necesita y continuar con su negocio.
Todo esto tiene un costo: cualquier condición de error causará un pánico y detener el programa. Los pánicos en Rust son irrecuperables por una razón: son una señal de que algo anda lo suficientemente mal como para indicar un error real en el programa.
Si utiliza .unwrap()
o uno de .unwrap()
variantes de, como .unwrap_or()
, tenga en cuenta que todavía tiene una capacidad limitada de manejo de errores. Debe pasar un valor de algún tipo que se ajuste al tipo an OK
valor produciría. Con match
tiene mucha más flexibilidad de comportamiento que simplemente producir algo del tipo adecuado.
Si cree que nunca necesitará esa flexibilidad en una ruta de programa determinada, .unwrap()
está bien. Sin embargo, todavía se recomienda intentar escribir un informe completo. match
primero para ver si ha pasado por alto algún aspecto del manejo.
Copyright © 2024 IDG Communications, Inc.