4.3.0 Before the Main Text
After learning Rust’s general programming concepts, you reach the most important part of all of Rust—ownership. It is quite different from other languages, and many beginners find it difficult to learn. This chapter aims to help beginners fully master this feature.
This chapter has three subsections:
- Ownership: Stack Memory vs. Heap Memory
- Ownership Rules, Memory, and Allocation
- Ownership and Functions (this article)
If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series.
4.3.1 Passing Values to Functions
In terms of semantics, passing a value to a function is similar to assigning a value to a variable, so to put it in one sentence: function parameter passing works the same way as assignment
Next, let’s explain it in detail: passing a value to a function will cause either a move or a copy.
- For data types that implement the Copy trait, a copy occurs, so the original variable is not affected and can continue to be used.
- For data types that do not implement the Copy trait, a move occurs, so the original variable is invalidated and cannot be used.
A detailed introduction to the Copy trait, moves, and copies was given in the previous article, 4.2. Ownership Rules, Memory, and Allocation, so it will not be repeated here.
fn main() {
let machine = String::from("6657");
wjq(machine);
let x = 6657;
wjq_copy(x);
println!("x is: {}", x);
}
fn wjq(some_string: String) {
println!("{}", some_string);
}
fn wjq_copy(some_number: i32) {
println!("{}", some_number);
}
-
For the variable
machine:-
Stringis a complex data type, allocated on the heap, and it does not implement the Copy trait. - When
machineis passed to thewjqfunction, a move occurs, meaning ownership is transferred from the variablemachineto the function parametersome_string. - At this point, ownership of
machinehas been transferred. The functionwjqcan use it normally, but the original variablemachineis no longer available. If you try to usemachineafterward, the compiler will report an error.
-
-
For the variable
x:-
i32is a basic data type with a fixed size, allocated on the stack, and it implements the Copy trait. - When
xis passed to thewjq_copyfunction, a copy occurs, meaning the value ofxis copied and passed to the function parametersome_number. - Because this is just a value copy, the original variable
xis unaffected and can still be used after the function call.
-
-
For the variable
some_string:- Its scope starts when it is declared on line 10 and ends when the
}on line 12 is reached. - When it leaves scope, Rust automatically calls the
dropfunction to free the memory occupied bysome_string.
- Its scope starts when it is declared on line 10 and ends when the
-
For the variable
some_number:- Its scope starts when it is declared on line 14 and ends when the
}on line 16 is reached. - Nothing special happens when it leaves scope, because types that implement the Copy trait do not call
Dropwhen they go out of scope.
- Its scope starts when it is declared on line 14 and ends when the
4.3.2 Return Values and Scope
Ownership is also transferred during the process of returning a value from a function.
fn main() {
let s1 = give_ownership();
let s2 = String::from("6657");
let s3 = takes_and_gives_back(s2);
}
fn give_ownership() -> String {
let some_string = String::from("machine");
some_string
}
fn takes_and_gives_back(a_string: String) -> String {
a_string
}
-
The behavior of the
give_ownershipfunction:- The
give_ownershipfunction creates aStringvariablesome_string, and ownership of it belongs to thegive_ownershipfunction. - When
some_stringis returned as the function’s return value, ownership is transferred to the caller, namely the variables1. - As a result,
some_stringwill not be dropped after leaving the scope ofgive_ownership, because its ownership has been handed over tos1.
- The
-
The behavior of the
takes_and_gives_backfunction:- The
takes_and_gives_backfunction accepts aStringparametera_string. When the function is called, ownership of the passed-in argument (s2) is transferred to the function parametera_string. - When the function returns
a_string, ownership is transferred once again froma_stringto the caller, namely the variables3. - At this point,
s2is no longer available, because its ownership has been transferred totakes_and_gives_back, and the function’s return value is assigned tos3.
- The
The ownership of a variable always follows the same pattern:
- Assigning a value to another variable causes a move. Only types that implement the Copy trait, such as basic types like
i32andf64, are copied during assignment. - When a variable containing heap data leaves scope, its value is cleaned up by the
dropfunction, unless ownership of the data has been moved to another variable.
4.3.3 Letting a Function Use a Value Without Taking Ownership
Sometimes the intent of the code is for a function to use a variable, but you do not want to lose the right to use the data as a result. In that case, you can write it like this:
fn main() {
let s1 = String::from("Hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length)
}
In this example, s1 has to give ownership to s, but when this function returns, it also returns s intact and hands ownership of the data to s2. In this way, ownership of the data is given back to a variable in the main function, allowing the data under s1 to be used again in main (even though the variable name has changed).
This approach is too troublesome and too clumsy. Rust provides a feature for this scenario called reference, which lets a function use a value without taking ownership of it. This feature will be explained in the next article.
Top comments (0)