In the fn main() function, the variable a is declared as an i8 container.
The value 4 was then assigned to the variable a.
a was then entered as an argument inside the function test()
The function test() was expected get ownership of the variable a aswell as returning a as a mutable variable with a different value. Here a was expected to have 14 as a value, instead in remained with 4, as shown by the println!.
The question :
If the function test(a) really took ownership of the variable a, then how does println!("{a}") not display 14 ? How can it still refer to 4 ?
This implies the question, to which scope is a function's returned value sent ? Is it sent to the scope of the function ? Or is it sent to the scope that the function is in ?
Besides, when I use the following program, which is the same, I get an error for doing the same thing that worked before. I can't see any difference.
fn string_uppercase(mut data: String) {
data = data.to_uppercase();
println!("{data}");
}
fn main() {
let data = "Rust is great!".to_string();
string_uppercase(data);
println!("{data}");
}
In this second program, I get an error saying that the value was borrowed after being moved. It's the same structure as the former program. Yet one works in an unintended way, and the other gives an error. I clearly miss what is the difference between the 2 programs.
The answer is that some types (like integers) implement the Copy trait, which allows move-by-copy, retaining the original ownership while also creating a copy that has a new owner.
String does not implement Copy. Moving a String requires a transfer of ownership. Thus the error.
Let's walk through this program. The first thing that happens is this line:
Which creates a variable a of type i8 with the value 4.
Then you call test, passing a to it. Since i8 is Copy, test() is called with a copy of the variable a.
Then test begins executing. Its parameter (which is 4_i8) is put into a local variable n1. This variable is local to the test function. test then adds 10 to its local n1, prints it, and then returns it. We return to main(), but what happens to the return value of test?
Well, since there's a semicolon after the expression test(a), the return value from test is dropped and simply ignored.
The original a value is not affected by any of this, and so printing a shows 4.
In your program using String, the difference is the previously mentioned Copy trait. Strings are not implicitly copied, so the string in data is moved into string_uppercase(), which means that the data variable cannot be used because the string has been moved.
string_uppercase uppercases the string it was given, but since it was moved into a local, the string is dropped at the end of string_uppercase.
The println! fails because data has been moved out of, so it cannot be accessed.
I would really like to give to test() the ownership of a and then display the newly owned a after the execution of the test() function. Would it be possible ?
Which does not work because main() does not allow to return anything (from what I understood by searching on the web).
So how could I change an immutable variable by transfering it's ownership to a function and make the function return it with the same name (for it to be displayed after being modified by the said function for example).
to be more precise, here would be the pseudo-program for what I was aiming :
fn main() {
let a = something
print(a) // displays something
change_to_something_else(a)
print(a) // displays something else
}
Thanks for the answer, shadowing was the thing I wanted to also avoid. This is because I was wondering if it was possible to do this by using a function to modify the original variable without shadowing it.
I added the pseudo-program lately in the former answer, it would be something like this :
fn main() {
let a = something
print(a) // displays something
change_to_something_else(a)
print(a) // displays something else
}
I really wonder if it would be possible, to give ownership of a variable to a function, then have this said function return the variable in the same scope as the original variable.
Thank you very much for this answer, I didn't even know that using * from inside a function in such a way was possible. That's indeed the closest to what I wanted to do.
I tried to modify to see if it could work without mutabilising the initial variable and it gave this :
Which gave an error, it is really impossible to make the immutable mutable, even if we transfer ownership to a function. This is very strange as even changing ownership is not above the initial immutable aspect of the declared variable.
Thanks again for this answer and have a very good day
Thank you very much for this answer, in this example, if I understand well, the variable a contains an exclusive reference to a value. That reference is not the value itself, therefore, *n1 would refer to the mutable value 4 and not a's value which would remain the same immutable exclusive reference to a mutable value. I probably didn't understand why there is &mut i8 and &mut 4 so I'll have to make tests with this. I however wonder if the last *n1 (the return value of test() would be necessary as I believed the return value was ignored due to the presence of a semi-colon at test(a);
After some tests, this version also worked (instead of let a:&mut i8 = &mut 4; there is let a = &mut 4;) :
fn test(n1:&mut i8) {
*n1 += 10;
}
fn main() {
let a = &mut 4;
test(a);
println!("{a}");
}
Just to clarify, it's not so much the semicolon that does this, as it's the fact that it's neither assigned to anything, not returned from the function. A function body (and any other block) is essentially a semicolon separated list of expressions and assign statements, where the last one becomes the return value:
{ let mut a = 4 ; test(&mut a) ; a } // works with a and returns it
When you put a semicolon at the end of the block, an implicit unit expression is added as the return expression by the compiler:
{ let mut a = 4 ; test(&mut a) ; println!("{a}") ; }
// ...is essentially...
{ let mut a = 4 ; test(&mut a) ; println!("{a}") ; () }
The exception to this is control flow expressions (if, for, etc.) that don't need the senicolon unless they are used in assignment or expressions that do require it.
You don't need to memorize all the details at this point, but I wanted to point it out so it doesn't cause any misconceptions.
Thank you very much for these detailed informations. It is very useful for me and probably other people to know these things as it helps me and probably other people to avoid confusion.