Naturally, the company’s new project forced me to learn Rust, but the day before I first encountered Rust, I was already cursing in my stomach. Then the star push now forced me to learn Rust, frustrated. But if you read it over and over again, slowly, you will understand what is naturally like!
I see the problem that causes all other confusion in Rust is Ownership, data stored on the stack or stored on the heap.
1. Rust data types.
In Rust divided into 2 groups of data types, this paragraph is only a version, read the code and understand, too lazy to write.
1.1 Scalar
Scalar
data types include:
- Integer Types
arch
means depending on the architecture of the computer running the program that it is 32-bits or 64-bits
- Floating-Point Types include:
f32
andf64
. - Boolean type:
bool
- Character type (Character type):
char
, this type of value is assigned only 1 character
1 2 3 4 5 6 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> c <span class="token operator">=</span> <span class="token char string">'z'</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> z <span class="token punctuation">:</span> <span class="token keyword">char</span> <span class="token operator">=</span> <span class="token char string">'ℤ'</span> <span class="token punctuation">;</span> <span class="token comment">// with explicit type annotation</span> <span class="token keyword">let</span> heart_eyed_cat <span class="token operator">=</span> '😻' <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
- Type
&str
: although this type is not fixed in size, its size must be determined at compile time.
1 2 3 4 5 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> <span class="token keyword">mut</span> s <span class="token operator">=</span> <span class="token string">"abc"</span> <span class="token punctuation">;</span> s <span class="token operator">=</span> <span class="token string">"abcde"</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Obviously from line 2 to line 3 the size of s
has increased but Compiler still determines the size at each stage during compilation.
1.2 Compound
Compound
data types include:
- Tuple
1 2 3 4 5 6 7 8 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> tup <span class="token operator">=</span> <span class="token punctuation">(</span> <span class="token number">500</span> <span class="token punctuation">,</span> <span class="token number">6.4</span> <span class="token punctuation">,</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> <span class="token punctuation">(</span> x <span class="token punctuation">,</span> y <span class="token punctuation">,</span> z <span class="token punctuation">)</span> <span class="token operator">=</span> tup <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"The value of y is: {y}"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
- Array
1 2 3 4 5 6 7 8 9 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token number">1</span> <span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">4</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">"January"</span> <span class="token punctuation">,</span> <span class="token string">"February"</span> <span class="token punctuation">,</span> <span class="token string">"March"</span> <span class="token punctuation">,</span> <span class="token string">"April"</span> <span class="token punctuation">,</span> <span class="token string">"May"</span> <span class="token punctuation">,</span> <span class="token string">"June"</span> <span class="token punctuation">,</span> <span class="token string">"July"</span> <span class="token punctuation">,</span> <span class="token string">"August"</span> <span class="token punctuation">,</span> <span class="token string">"September"</span> <span class="token punctuation">,</span> <span class="token string">"October"</span> <span class="token punctuation">,</span> <span class="token string">"November"</span> <span class="token punctuation">,</span> <span class="token string">"December"</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{:?}"</span> <span class="token punctuation">,</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{:?}"</span> <span class="token punctuation">,</span> b <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Note that arrays in rust won’t have any methods like pop or push to change the length of the array, or even implementing code like this:
1 2 3 4 5 6 7 8 9 10 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> <span class="token keyword">mut</span> a <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token number">1</span> <span class="token punctuation">,</span> <span class="token number">2</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">,</span> <span class="token number">4</span> <span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> b <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token string">"January"</span> <span class="token punctuation">,</span> <span class="token string">"February"</span> <span class="token punctuation">,</span> <span class="token string">"March"</span> <span class="token punctuation">,</span> <span class="token string">"April"</span> <span class="token punctuation">,</span> <span class="token string">"May"</span> <span class="token punctuation">,</span> <span class="token string">"June"</span> <span class="token punctuation">,</span> <span class="token string">"July"</span> <span class="token punctuation">,</span> <span class="token string">"August"</span> <span class="token punctuation">,</span> <span class="token string">"September"</span> <span class="token punctuation">,</span> <span class="token string">"October"</span> <span class="token punctuation">,</span> <span class="token string">"November"</span> <span class="token punctuation">,</span> <span class="token string">"December"</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> a <span class="token punctuation">[</span> <span class="token number">5</span> <span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token number">6</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{:?}"</span> <span class="token punctuation">,</span> a <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{:?}"</span> <span class="token punctuation">,</span> b <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Although when compile does not detect errors, but when running, it will give an error:
1 2 3 | thread <span class="token string">'main'</span> panicked at <span class="token string">'index out of bounds: the len is 5 but the index is 5'</span> , ./file.rs:4:5 note: run with <span class="token variable"><span class="token variable">`</span> <span class="token assign-left variable">RUST_BACKTRACE</span> <span class="token operator">=</span> <span class="token number">1</span> <span class="token variable">`</span></span> environment variable to display a backtrace |
2. Stack and Heap
In Rust, memory is divided into two parts, Stack
and Heap
Stack
works in the following way: Last In First Out (push, pop), the data stored on theStack
must be data of known size at compile time and known-fixed size. during program runtime.Heap
is for data of unknown size and possible size while running the program (unknown size), then pointers to those data are stored on theStack
. TheHeap
works thanks to theMemory Allocator
, every time a certain data wants to be stored on theHeap
,Memory Allocator
will find just enough area on theHeap
to store the data in, and then push the pointer to theStack
.
So what data types are stored directly on the Stack? Including: integer types, floating-point types, char, &str, compound types whose members only include integer, floating-point, char, &str, these types when stored and retrieved are simply push Stack
.
As for data types such as String
, Vector
, compound types whose members contain String
or Vector
, self-defined structs whose members contain String
or Vector
will be stored on the Heap
.
This part is starting to hurt my head, like I just finished a cup of super concentrated Phuc Long tea.
The Three Rules of Ownership
- Every value in Rust has an
owner
, no matter what data type the value is. - A value cannot have more than one
owner
at a time. - When the
owner
goes out of its scope, the value it is carrying will also bedrop
.
What is the scope of a variable?
For example:
1 2 3 4 5 6 7 8 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">{</span> <span class="token comment">// s is not valid here, it’s not yet declared</span> <span class="token keyword">let</span> s <span class="token operator">=</span> <span class="token string">"hello"</span> <span class="token punctuation">;</span> <span class="token comment">// s is valid from this point forward</span> <span class="token comment">// do stuff with s</span> <span class="token punctuation">}</span> <span class="token comment">// this scope is now over, and s is no longer valid</span> <span class="token punctuation">}</span> |
If you read the code, you can imagine it, the scope of the variable s
is determined by the nearest pair of curly braces {}
that cover the declaration of the variable s
, where s
is the owner
of the value "hello"
and this value as said above, it is stored on the Stack
.
3. Illustration Ownership
Again, in Rust, String
and &str
are two completely different data types, although both are for storing string literals, &str
is a built-in Rust type and String
is a code one must code to build it based on. Vector
, and has a set of methods of its own.
Why is it necessary to need String
when there is &str
, as mentioned above, &str
although it can change length when running the program, Compiler always determines its length at each passing time. compile process, and String
will be for the values that are entered by the user when the program runs, so Compile cannot know the length of that value in advance, so it must use String
. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">use</span> <span class="token namespace">std <span class="token punctuation">::</span></span> io <span class="token punctuation">;</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"Please input your text."</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> <span class="token keyword">mut</span> text <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">new</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token namespace">io <span class="token punctuation">::</span></span> <span class="token function">stdin</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">read_line</span> <span class="token punctuation">(</span> <span class="token operator">&</span> <span class="token keyword">mut</span> text <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">expect</span> <span class="token punctuation">(</span> <span class="token string">"Failed to read line"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"You text: {text}"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
String
data will have the value stored on the Heap
.
1 2 3 4 5 | <span class="token attribute attr-name">#![allow(unused)]</span> <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> s1 <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">from</span> <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Normally when we declare a variable but then do nothing about it, when Compiler compiles it will show a warning
= note:
#[warn(unused_variables)]on by default
, we can fix it by changing variable name to_s1
or add#![allow(unused)]
if usingstd
or#[allow(unused)]
if not usingstd
.
s1
will be stored in memory:
s1
is a data set stored on the Stack
including ptr
, len
, capacity
, ptr
pointing to the actual value "hello"
being stored on the Heap
, this data set represents that s1
is the owner of the value "hello"
.
completely different from the following case:
1 2 3 4 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Since x
is an i32
type, the size is known in advance and does not change the size, so the value of x
will be stored entirely on the Stack
, and x
is the owner of that value.
Assignment (Shallow copy)
As a way to copy data on the Stack
, consider the following example with String
:
1 2 3 4 5 6 7 8 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> s1 <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">from</span> <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> s2 <span class="token operator">=</span> s1 <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}, world!"</span> <span class="token punctuation">,</span> s1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// ERROR</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}, world!"</span> <span class="token punctuation">,</span> s2 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
When we assign the value of s1
to s2
we are actually moving the ownership of the value "hello"
on the Heap
( ownership
) from s1
to s2
, so since line 3, s1
has no value, so when printing it will show an error on the screen, Compiler will detect this error at compile time, the error is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $ cargo run Compiling ownership v0.1.0 <span class="token punctuation">(</span> file:///projects/ownership <span class="token punctuation">)</span> error <span class="token punctuation">[</span> E0382 <span class="token punctuation">]</span> : borrow of moved value: <span class="token variable"><span class="token variable">`</span> s1 <span class="token variable">`</span></span> -- <span class="token operator">></span> src/main.rs:5:28 <span class="token operator">|</span> <span class="token number">2</span> <span class="token operator">|</span> <span class="token builtin class-name">let</span> s1 <span class="token operator">=</span> String::from <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token operator">|</span> -- move occurs because <span class="token variable"><span class="token variable">`</span> s1 <span class="token variable">`</span></span> has <span class="token builtin class-name">type</span> <span class="token variable"><span class="token variable">`</span> String <span class="token variable">`</span></span> , <span class="token function">which</span> does not implement the <span class="token variable"><span class="token variable">`</span> Copy <span class="token variable">`</span></span> trait <span class="token number">3</span> <span class="token operator">|</span> <span class="token builtin class-name">let</span> s2 <span class="token operator">=</span> s1 <span class="token punctuation">;</span> <span class="token operator">|</span> -- value moved here <span class="token number">4</span> <span class="token operator">|</span> <span class="token number">5</span> <span class="token operator">|</span> println <span class="token operator">!</span> <span class="token punctuation">(</span> <span class="token string">"{}, world!"</span> , s1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token operator">|</span> ^^ value borrowed here after move <span class="token operator">|</span> <span class="token operator">=</span> note: this error originates <span class="token keyword">in</span> the macro <span class="token variable"><span class="token variable">`</span> $crate::format_args_nl <span class="token variable">`</span></span> <span class="token punctuation">(</span> in Nightly builds, run with -Z macro-backtrace <span class="token keyword">for</span> <span class="token function">more</span> info <span class="token punctuation">)</span> For <span class="token function">more</span> information about this error, try <span class="token variable"><span class="token variable">`</span> rustc --explain E0382 <span class="token variable">`</span></span> <span class="token builtin class-name">.</span> error: could not compile <span class="token variable"><span class="token variable">`</span> ownership <span class="token variable">`</span></span> due to previous error |
Consider the following example with i32
:
1 2 3 4 5 6 7 8 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> y <span class="token operator">=</span> x <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> x <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> y <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
This code is completely valid and runs normally, because i32
is a valid data type stored on the Stack
.
The assignment aka
Shallow copy
will only copy the data on theStack
, which will perform the following different behaviors:
- For single data types of known size such as
interger
,floating-point
,&str
, the value is stored on theStack
, so when performing the assignment, the value will be copied to another copy also on theStack
. does not violate any ofOwnership
‘s principles, as in the example above,x
is still valid aftery
is set tox
.- For data types of unknown size such as
String
andVector
, the value is actually stored on theHeap
andpointer
len
capacity
is stored on theStack
, so when performing the assignment of the data on thestack
, it will be copied as another copy on the stack.stack
, but recallingOwnership
‘s 2nd rule is “A value cannot have more than oneowner
at a time.”, so for these data types, the assignment will bemove ownership
. from one variable to another. So with theString
example above, when assignings2
tos1
,s1
will no longer have any value, the commandpritnln!("{}", s1)
will get an error.
Clone (Deep copy)
The clone
is a way of copying values on the Heap
, consider the following example:
1 2 3 4 5 6 7 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> s1 <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">from</span> <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> s2 <span class="token operator">=</span> s1 <span class="token punctuation">.</span> <span class="token function">clone</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"s1 = {}, s2 = {}"</span> <span class="token punctuation">,</span> s1 <span class="token punctuation">,</span> s2 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Now s1
, s2
are 2 owner
of 2 completely separate values on the Heap
, so s1
is still valid after performing let s2 = s1.clone()
Consider the example with i32
:
1 2 3 4 5 6 7 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> b <span class="token operator">=</span> a <span class="token punctuation">.</span> <span class="token function">clone</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}, {}"</span> <span class="token punctuation">,</span> a <span class="token punctuation">,</span> b <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Although, the i32
data type stores the value on the Stack
absolutely nothing on the Heap
, but clone()
still works, because it determines the Stack
layer is the deepest layer of the i32
type, hence the clone()
operation. clone()
is exactly the same as assignment in this case.
Oke, I just added a bowl of pho with 2 tablespoons of MSG.
Ownership with Function
Consider the following example with String
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> s <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">from</span> <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// s comes into scope</span> <span class="token function">takes_ownership</span> <span class="token punctuation">(</span> s <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// s's value moves into the function...</span> <span class="token comment">// ... and so is no longer valid here</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> s <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// ERROR</span> <span class="token punctuation">}</span> <span class="token keyword">fn</span> <span class="token function-definition function">takes_ownership</span> <span class="token punctuation">(</span> some_string <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// some_string comes into scope</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> some_string <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Here, some_string goes out of scope and `drop` is called. The backing</span> <span class="token comment">// memory is freed.</span> |
In the first line, we declare s
as a variable of type String
, s
is now the owner
of the value "hello"
being stored on the Heap
.
In the next line we call the function takes_ownership(s)
with the parameter passed as s
, looking at the declaration of the function as fn takes_ownership(some_string: String) String{...}
, we can roughly understand the first one. assign some_string = s
now the ownership
of the value "hello"
has been transferred from s
to some_string
, then run the code of the function with some_string
, otherwise recalling the knowledge of the variable scope
then the scope
of some_string
is only internal to the take_ownership
function , at the end of this function some_string
will be drop
, so the command pritnln!("{}", s)
will fail because s
is empty.
Consider the example with i32
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> x <span class="token operator">=</span> <span class="token number">5</span> <span class="token punctuation">;</span> <span class="token comment">// x comes into scope</span> <span class="token function">makes_copy</span> <span class="token punctuation">(</span> x <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// x would move into the function,</span> <span class="token comment">// but i32 is Copy, so it's okay to still</span> <span class="token comment">// use x afterward</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> x <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">fn</span> <span class="token function-definition function">makes_copy</span> <span class="token punctuation">(</span> some_integer <span class="token punctuation">:</span> <span class="token keyword">i32</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// some_integer comes into scope</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> some_integer <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Here, some_integer goes out of scope. Nothing special happens.</span> |
In the first line, it is still declared that x
is the owner
of the value 5
stored on the Stack
.
In the next line, we call the function makes_copy(x)
, with the input parameter x
, roughly understood as the first assignment of some_integer = x
, recalling the knowledge about the assignment with type i32
, then in fact x
and some_integer
is being 2 owner
of 2 separate values on Stack
, next we execute the code of makes_copy()
function for some_integer
, the end of some_integer
function will be drop
, but due to ownership
of x
and some_integer
completely located separate, so even if some_integer
is drop
, x
is still valid. So the above code is completely error free.
How to preserve ownership of String when calling a function?
We simply give the return function an ownership
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> s1 <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">from</span> <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// s2 comes into scope</span> <span class="token keyword">let</span> s1 <span class="token operator">=</span> <span class="token function">takes_and_gives_back</span> <span class="token punctuation">(</span> s1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> s1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// This function takes a String and returns one</span> <span class="token keyword">fn</span> <span class="token function-definition function">takes_and_gives_back</span> <span class="token punctuation">(</span> a_string <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span> <span class="token comment">// a_string comes into</span> <span class="token comment">// scope</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> a_string <span class="token punctuation">)</span> <span class="token punctuation">;</span> a_string <span class="token comment">// a_string is returned and moves out to the calling function</span> <span class="token punctuation">}</span> |
The first will probably assign a_string = s1
, the ownership
of "hello"
is now a_string
from s1
to move
, but the return value of the function is now String
, so when leaving the ownership
function, it is move
right from a_string
returns s1
.
Why do we have to let s1
twice, because by default the variables in Rust are immutable
, so s1
‘s data is not allowed to be reassigned, so we have to choose to over-declare the variable s1
, to overcome this we can Edit the code as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> <span class="token keyword">mut</span> s1 <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">from</span> <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// s2 comes into scope</span> s1 <span class="token operator">=</span> <span class="token function">takes_and_gives_back</span> <span class="token punctuation">(</span> s1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> s1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// This function takes a String and returns one</span> <span class="token keyword">fn</span> <span class="token function-definition function">takes_and_gives_back</span> <span class="token punctuation">(</span> a_string <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token class-name">String</span> <span class="token punctuation">{</span> <span class="token comment">// a_string comes into</span> <span class="token comment">// scope</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"{}"</span> <span class="token punctuation">,</span> a_string <span class="token punctuation">)</span> <span class="token punctuation">;</span> a_string <span class="token comment">// a_string is returned and moves out to the calling function</span> <span class="token punctuation">}</span> |
Simply add mut
to the s1
declaration.
At this point, I just need to add a few lights, a little music, and I can go to the scene.
But it would be cumbersome if we had to return ownership
every function along with the calculated value that we want the function to return, like the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">fn</span> <span class="token function-definition function">main</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> s1 <span class="token operator">=</span> <span class="token class-name">String</span> <span class="token punctuation">::</span> <span class="token function">from</span> <span class="token punctuation">(</span> <span class="token string">"hello"</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">let</span> <span class="token punctuation">(</span> s2 <span class="token punctuation">,</span> len <span class="token punctuation">)</span> <span class="token operator">=</span> <span class="token function">calculate_length</span> <span class="token punctuation">(</span> s1 <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token macro property">println!</span> <span class="token punctuation">(</span> <span class="token string">"The length of '{}' is {}."</span> <span class="token punctuation">,</span> s2 <span class="token punctuation">,</span> len <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">fn</span> <span class="token function-definition function">calculate_length</span> <span class="token punctuation">(</span> s <span class="token punctuation">:</span> <span class="token class-name">String</span> <span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token punctuation">(</span> <span class="token class-name">String</span> <span class="token punctuation">,</span> <span class="token keyword">usize</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">let</span> length <span class="token operator">=</span> s <span class="token punctuation">.</span> <span class="token function">len</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// len() returns the length of a String</span> <span class="token punctuation">(</span> s <span class="token punctuation">,</span> length <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Fortunately, Rust has support for us References and Borrowing
, which will give us access to the value of the variable without affecting the ownership
of the variable. I’ll just introduce it for now, but wait until I’m excited to continue writing about this.
4. Summary
To summarize, there are a few ideas:
- Data types with known sizes will be stored on the
Stack
- Unknown data types will be stored on the
Heap
- The assignment is
Shallow copy
, which only copies the value on the Stack. Performing an assignment with a data type of known size will create a newownership
separate from the old one, performing an assignment with a data type of unknown size willmove ownership
ownership
the old variable to the new variable- The
clone()
operation isDeep copy
, copies values on theHeap
,clone()
can work with both known and unknown data types. How funny, how strange!- When a variable goes out of its scope, it is
drop
- To preserve an
ownership
when calling a function, that function either returns anotherownership
or the parameter passed to the function is in the form ofreference
.