The core concept of Rust is Ownership
, a GC-enabled language that allows the runtime to sweep the heap at runtime and release unreferenced garbage objects, such as go. For languages like c/c++, you need to manage the allocation and release of memory yourself.
Rust uses the concept of Ownership
and appends various check rules to the compiler to implement memory management. Note that most of Rust’s work is done at compile time, so there is no additional overhead at runtime. Here are three principles.
- each value, has an owner
- At the same time, a value can have only one owner
- When the owner leaves the scope, the corresponding value is automatically droppe
RAII
Let’s first look at leaving the scope and releasing it automatically.
The simplest code, just one line, allocates the string on the heap and disassembles it to observe how memory is managed.
|
|
The assembly on line 2 calls core::convert::From
to create the string variable. Then on line 3, at the end of main, core::ptr::drop_in_place
is called automatically to free the string.
The automatic destructuring out of scope is much like c++’s RAII
(Resource Acquisition Is Initialization), except that rust calls drop trait
out of scope.
Ownership
This code will definitely work with go, but not with rust.
|
|
The execution reports an error because the ownership of the s1 value has been moved to s2, and the original s1 is no longer available. move occurs because s1
has type String
, which does not implement the Copy
trait.
You can see the result of the string, just like go, the string header has pointer pointing to heap memory. If s1, s2 are shallow copies and the pointer points to data on the heap, then the same piece of memory will be freed twice after leaving scope!
You can call s1.clone()
to make a deep copy to solve this problem. But it is inefficient to make a memory copy every time, so the concept of reference references
will be introduced later.
Ownership and functions
|
|
This is an example from the website. When takes_ownership
is executed, the ownership of the value corresponding to s is transferred to the function and released when it leaves the scope, so if the main function tries to use it again, it will report an error.
But at the same time, x is an int value, and the makes_copy
function will copy this value, not transfer ownership.
Move,Copy,Clone
In Rust, if the type does not implement Copy
, then the type is assigned, passed, and returned with Move
semantics, which is a bit awkward and not intuitive.
The previous string is a Move
, so let’s look at an integer example.
Here s1 is the i32 type by default, which implements the Copy
semantics, so s1 can be used afterwards.
To see which types implement Copy
semantics by default, they are generally scalars, i.e. types allocated on the stack and sized at compile time.
- All the integer types, such as u32.
- The Boolean type, bool, with values true and false.
- All the floating point types, such as f64.
- The character type, char.
- Tuples, if they only contain types that also implement Copy. For example, (i32, i32) implements Copy, but (i32, String) does not.
So how is it handled for custom types, such as struct?
|
|
As you can see, the error is reported because d has been moved when test
is called, so the variable d can no longer be used in main.
But the problem is that struct Data members are all integers, and they all implement Copy
by default.
Here you need to mark the struct #[derive(Copy, Clone)]
, so that the custom type automatically implements the Copy trait
, which requires that all fields have already implemented Copy
.
|
|
For struct S2
, since S2 has String type in its field, String type does not implement Copy trait
, so S2 type cannot implement Copy trait
.
S2 also contains E1 type, which does not implement Clone
and Copy trait
, but we can implement Clone trait
for S2 type ourselves, and generate a new E1 instance in Clone::clone
method, which can clone a new S2 instance.