Rust by Example — страница 32 из 66

==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!

Destructor

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

See also:

Box

Ownership and moves

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

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;

// Both values can be independently used

println!("x is {}, and y is {}", x, y);

// `a` is a pointer to a _heap_ allocated integer

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

println!("a contains: {}", a);

// *Move* `a` into `b`

let b = a;

// The pointer address of `a` is copied (not the data) into `b`.

// Both are now pointers to the same heap allocated data, but

// `b` now owns it.

// Error! `a` can no longer access the data, because it no longer owns the

// heap memory

//println!("a contains: {}", a);

// TODO ^ Try uncommenting this line

// This function takes ownership of the heap allocated memory from `b`

destroy_box(b);

// Since the heap memory has been freed at this point, this action would

// result in dereferencing freed memory, but it's forbidden by the compiler

// Error! Same reason as the previous Error

//println!("b contains: {}", b);

// TODO ^ Try uncommenting this line

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Mutability

Mutability of data can be changed when ownership is transferred.

fn main() {

let immutable_box = Box::new(5u32);

println!("immutable_box contains {}", immutable_box);

// Mutability error

//*immutable_box = 4;

// *Move* the box, changing the ownership (and mutability)

let mut mutable_box = immutable_box;

println!("mutable_box contains {}", mutable_box);

// Modify the contents of the box

*mutable_box = 4;

println!("mutable_box now contains {}", mutable_box);

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Partial moves

Pattern bindings can have by-move and by-reference bindings at the same time which is used in destructuring. Using these pattern will result in partial move for the variable, which means that part of the variable is moved while other parts stayed. In this case, the parent variable cannot be used afterwards as a whole. However, parts of it that are referenced and not moved can be used.

fn main() {

#[derive(Debug)]

struct Person {

name: String,

age: u8,

}

let person = Person {

name: String::from("Alice"),

age: 20,

};

// `name` is moved out of person, but `age` is referenced

let Person { name, ref age } = person;

println!("The person's age is {}", age);

println!("The person's name is {}", name);

// Error! borrow of partially moved value: `person` partial move occurs

//println!("The person struct is {:?}", person);

// `person` cannot be used but `person.age` can be used as it is not moved