Gestión de recursos RAII en Rust

RustRustBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

En este laboratorio, exploraremos el concepto de RAII en Rust, que fuerza que la adquisición de recursos sea la inicialización. Esto significa que cuando los objetos salen del ámbito, se llaman a sus destructores y se liberan los recursos que poseen, eliminando la necesidad de la gestión manual de memoria y asegurando la protección contra errores de fuga de recursos. También aprenderemos sobre el trato Drop en Rust, que permite implementar lógica de destructor personalizada para los tipos que lo requieren.

Nota: Si el laboratorio no especifica un nombre de archivo, puede usar cualquier nombre de archivo que desee. Por ejemplo, puede usar main.rs, compilarlo y ejecutarlo con rustc main.rs &&./main.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/ControlStructuresGroup(["Control Structures"]) rust(("Rust")) -.-> rust/MemorySafetyandManagementGroup(["Memory Safety and Management"]) rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/AdvancedTopicsGroup(["Advanced Topics"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/DataTypesGroup -.-> rust/string_type("String Type") rust/ControlStructuresGroup -.-> rust/for_loop("for Loop") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/MemorySafetyandManagementGroup -.-> rust/lifetime_specifiers("Lifetime Specifiers") rust/MemorySafetyandManagementGroup -.-> rust/drop_trait("Drop Trait") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") rust/AdvancedTopicsGroup -.-> rust/traits("Traits") subgraph Lab Skills rust/variable_declarations -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/string_type -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/for_loop -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/function_syntax -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/expressions_statements -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/lifetime_specifiers -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/drop_trait -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/method_syntax -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} rust/traits -.-> lab-99194{{"Gestión de recursos RAII en Rust"}} end

RAII

Las variables en Rust hacen más que solo almacenar datos en la pila: también poseen recursos, por ejemplo, Box<T> posee memoria en el montón. Rust fuerza el RAII (Resource Acquisition Is Initialization), por lo que siempre que un objeto sale del ámbito, se llama a su destructor y se liberan los recursos que posee.

Este comportamiento protege contra los errores de fuga de recursos, ¡así que nunca tendrás que liberar manualmente la memoria ni preocuparte por fugas de memoria nuevamente! Aquí hay un rápido demostración:

// raii.rs
fn create_box() {
    // Asignar un entero en el montón
    let _box1 = Box::new(3i32);

    // `_box1` se destruye aquí, y la memoria se libera
}

fn main() {
    // Asignar un entero en el montón
    let _box2 = Box::new(5i32);

    // Un ámbito anidado:
    {
        // Asignar un entero en el montón
        let _box3 = Box::new(4i32);

        // `_box3` se destruye aquí, y la memoria se libera
    }

    // Crear muchos boxes solo por diversión
    // No es necesario liberar manualmente la memoria!
    for _ in 0u32..1_000 {
        create_box();
    }

    // `_box2` se destruye aquí, y la memoria se libera
}

Por supuesto, podemos comprobar doblemente errores de memoria usando valgrind:

<!-- REUSE-IgnoreStart -->
<!-- Prevent REUSE from parsing the copyright statement in the sample code -->
$ rustc raii.rs && valgrind./raii
==26873== Memcheck, a memory error detector
==26873== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26873== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==26873== Command:./raii
==26873==
==26873==
==26873== HEAP SUMMARY:
==26873==     in use at exit: 0 bytes in 0 blocks
==26873==   total heap usage: 1,013 allocs, 1,013 frees, 8,696 bytes allocated
==26873==
==26873== All heap blocks were freed -- no leaks are possible
==26873==
==26873== For counts of detected and suppressed errors, rerun with: -v
==26873== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
<!-- REUSE-IgnoreEnd -->

¡No hay fugas aquí!

Destructor

La noción de destructor en Rust se proporciona a través del trato [Drop]. El destructor se llama cuando el recurso sale del ámbito. No es necesario implementar este trato para cada tipo, solo impleméntelo para su tipo si requiere su propia lógica de destructor.

Ejecute el siguiente ejemplo para ver cómo funciona el trato [Drop]. Cuando la variable en la función main sale del ámbito, se invocará el destructor personalizado.

struct ToDrop;

impl Drop for ToDrop {
    fn drop(&mut self) {
        println!("ToDrop está siendo eliminado");
    }
}

fn main() {
    let x = ToDrop;
    println!("Creado un ToDrop!");
}

Resumen

¡Felicitaciones! Has completado el laboratorio de Raii. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.