Выбрать главу

Scopes play an important part in ownership, borrowing, and lifetimes. That is, they indicate to the compiler when borrows are valid, when resources can be freed, and when variables are created or destroyed.

Variables in Rust do more than just hold data in the stack: they also own resources, e.g. Box<T> owns memory in the heap. Rust enforces RAII (Resource Acquisition Is Initialization), so whenever an object goes out of scope, its destructor is called and its owned resources are freed.

This behavior shields against resource leak bugs, so you'll never have to manually free memory or worry about memory leaks again! Here's a quick showcase:

// raii.rs

fn create_box() {

// Allocate an integer on the heap

let _box1 = Box::new(3i32);

// `_box1` is destroyed here, and memory gets freed

}

fn main() {

// Allocate an integer on the heap

let _box2 = Box::new(5i32);

// A nested scope:

{

// Allocate an integer on the heap

let _box3 = Box::new(4i32);

// `_box3` is destroyed here, and memory gets freed

}

// Creating lots of boxes just for fun

// There's no need to manually free memory!

for _ in 0u32..1_000 {

create_box();

}

// `_box2` is destroyed here, and memory gets freed

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Of course, we can double check for memory errors using valgrind:

$ 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)

No leaks here!

The notion of a destructor in Rust is provided through the Drop trait. The destructor is called when the resource goes out of scope. This trait is not required to be implemented for every type, only implement it for your type if you require its own destructor logic.

Run the below example to see how the Drop trait works. When the variable in the main function goes out of scope the custom destructor will be invoked.

struct ToDrop;

impl Drop for ToDrop {

fn drop(&mut self) {

println!("ToDrop is being dropped");

}

}

fn main() {

let x = ToDrop;

println!("Made a ToDrop!");

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Box

Because variables are in charge of freeing their own resources, resources can only have one owner. This also prevents resources from being freed more than once. Note that not all variables own resources (e.g. references).

When doing assignments (let x = y) or passing function arguments by value (foo(x)), the ownership of the resources is transferred. In Rust-speak, this is known as a move.

After moving resources, the previous owner can no longer be used. This avoids creating dangling pointers.

// This function takes ownership of the heap allocated memory

fn destroy_box(c: Box<i32>) {

println!("Destroying a box that contains {}", c);

// `c` is destroyed and the memory freed

}

fn main() {

// _Stack_ allocated integer

let x = 5u32;

// *Copy* `x` into `y` - no resources are moved

let y = x;