Reputation: 1079
I want a function that calls other functions that are mutually recursive, but I already have that with type signatures like:
fn f1(mut index: &mut usize, ..)
fn f2(mut index: &mut usize, ..)
I really want a set of mutually recursive functions that can only mutate the index
variable I've defined in my main()
function, and are not capable of pointing to any other variable.
I've read the 2 answers from What's the difference in `mut` before a variable name and after the `:`?, and I've tried several approaches but still I can't achieve what I want. I think I don't really understand the concepts.
Here is evidence of my current understanding:
// with &mut I can change the referred value of n, but then I can't
// pass a mutable reference anywhere
fn mutate_usize_again(n: &mut usize) {
*n += 1;
// n += 70; ^ cannot use `+=` on type `&mut usize`
}
fn mutate_usize_two_times(mut n: &mut usize) {
*n = 8;
// if I don't write mut n, I can't pass a mutable reference to
// the mutate_usize_again function
mutate_usize_again(&mut n);
}
fn mutate_usize_one_time_referred_value(n: &mut usize) {
*n += 25;
}
// this changes the referred value of n
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
// doesn't work because of lifetimes
// this changes where is pointing a (Copy?) reference of n
// passed value does not change
/*
fn mutate_usize_one_time(mut n: &usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = &48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
*/
fn main() {
let mut index = 0;
mutate_usize_one_time_mutable_pointer(index);
println!("index after mutate_usize_one_time_mutable_pointer = {}", index);
mutate_usize_two_times(&mut index);
println!("index after mutate_usize_two_times = {}", index);
mutate_usize_one_time_referred_value(&mut index);
println!("index after mutate_usize_ = {}", index);
}
I would really appreciate a nice explanation of what is going on in my code if I've misunderstood.
I'm starting to think that what I want is already done given:
index
must refer to its updated value => mut index
index
must be capable of changing the referred value and pass mutable references to the other functions. => &mut usize
(mut index2: &mut usize)
, the compiler would not let me have 2 mutable references pointing to the same memory location.Upvotes: 0
Views: 1442
Reputation: 602
Considering your title, you're confused on the very principle. Short answer that strikes it all:
You shall always follow the even rule. You have same amount of slashes and same amount of dereferences. Together they give an even number.
Upvotes: -5
Reputation: 4031
A reference is a variable that points to something else. References do not collapse, so in Rust you can have a reference to a reference.
Your linked question describes what's different between x: &mut T
and mut x: T
. mut x: &T
means you can change where the reference points to, but you can't change the value what it points to.
Non-reference values will be copied when passed to functions (or moved, if not implementing Copy
).
A working example:
// with &mut I can change the referred value of n
fn mutate_usize_again(n: &mut usize) {
*n += 1;
*n += 70;
}
fn mutate_usize_two_times(n: &mut usize) {
*n = 8;
// just pass it along
mutate_usize_again(n);
}
fn mutate_usize_one_time_referred_value(n: &mut usize) {
*n += 25;
}
// this changes the **local** value of n
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let mut index = 0;
mutate_usize_one_time_mutable_pointer(index);
println!("index after mutate_usize_one_time_mutable_pointer = {}", index);
mutate_usize_two_times(&mut index);
println!("index after mutate_usize_two_times = {}", index);
mutate_usize_one_time_referred_value(&mut index);
println!("index after mutate_usize_one_time_referred_value = {}", index);
}
Upvotes: 2
Reputation: 300229
Ah! The old pointer/pointee issue. Don't worry, it's a common stumbling block. Keep at it, at some point it'll click and then it'll just seem obvious.
What is a reference?
A reference is an indirection. The address of a thing that exists somewhere else.
Let's use an analogy:
For the moment, let's forget about types, stack and heap: we only have closets and drawers:
Note: it is nonsensical to mention just a drawer ID, all closets have a drawer 0...
How to use a drawer?
Let us imagine a (typeless) simple example, the addition function:
fn add(left, right) { left + right }
Now, what happens when we call add(3, 4)
?
Function
call
+-add-+ <-- New closet
| 3 | <-- Drawer 0: value 3
+-----+
| 4 | <-- Drawer 1: value 4
+-----+
Note: in the following, I'll represent a closet as an array. This closet would be [3, 4]
. I'll also "number" the closets using letters, to avoid mixing closets and drawers, so this closet could be d
, and the first drawer of d
would be 0@d
(which contains 3 here).
The function is defined as "take the value in drawer 0, take the value in drawer 1, add them together in drawer 2, return content of drawer 2".
Let's spice things up; and introduce references:
fn modify() {
let x = 42;
let y = &x;
*y = 32;
}
What does modify
do?
modify
=> a fresh closet is given to us a: []
,let x = 42;
=> our closet is now a: [42]
,let y = &x;
=> our closet is now a: [42, 0@a]
,And now comes the crux: *y = 32;
. This means put 32 in the drawer pointed to by y
. And thus the closet is now: a: [32, 0@a]
.
Your first example
Let's look at your first example, put in situation with a main
:
// with &mut I can change the referred value of n, but then I can't
// pass a mutable reference anywhere
fn mutate_usize_again(n: &mut usize) {
*n += 1;
// n += 70; ^ cannot use `+=` on type `&mut usize`
}
fn main() {
let mut x = 24;
mutate_usize_gain(&mut x);
}
So, what's going on here?
main
=> a fresh closet is allocated a: []
,let mut x = 24;
=> a: [24]
,mutate_usize_again
=> a fresh closet allocated b: []
,&mut x
=> b: [0@a]
,*n += 1;
=> add 1 to the drawer pointed to by n
(0@a), this means that b
does not change, but a
does! Now a: [25]
.n += 70
=> add 70 to a reference... this does not make sense, a reference does not have arithmetic operations.Your second example
Let's move on to your second example, augmented:
// Parameter renamed because it's confusing to always use the same letter
fn mutate_usize_again(z: &mut usize) { *z += 1; }
fn mutate_usize_two_times(mut n: &mut usize) {
*n = 8;
// if I don't write mut n, I can't pass a mutable reference to
// the mutate_usize_again function
mutate_usize_again(&mut n);
}
fn main() {
let mut x = 24;
mutate_usize_two_times(&mut x);
}
We'll need 3 closets!
main
=> a fresh closet is allocated a: []
,let mut x = 24
=> a: [24]
,mutate_usize_two_times
=> a fresh closet is allocated b: []
,n: &mut usize = &mut x
=> b: [0@a]
,*n = 8
=> stores 8 in the drawer pointed to by n
: a: [8]
, b
unchanged,mutate_usize_again
=> a fresh closet is allocated c: []
,z: &mut usize = &mut n
=> ERROR. The type of &mut n
is &mut &mut usize
which cannot be assigned to a parameter of type z
,z: &mut usize = n
=> c: [0@a]
,*z += 1
=> adds 1 to the value in the drawer pointed to by z
: a: [9]
, b
and c
unchanged.You can take a reference to a reference, but it's not what you meant to do here. We can indeed twist the example to making it work though by defining:
fn mutate_usize_again(z: &mut &mut usize) { **z += 1; }
^ ^~~ dereference TWICE
|~~~~~~~~~~~~~~ two levels of reference
In this case, starting again from the call to mutate_usize_again
:
a: [8]
, b: [0@a]
mutate_usize_again
=> a fresh closet is allocated c: []
,z: &mut &mut usize = &mut n
=> c: [0@b]
<- a reference to a reference!**z += 1
=> adds 1 to the value in drawer pointed to by the reference pointed to by z
. **z += 1
is **0@b += 1
which is *0@a += 1
, which modifies a
to be a: [9]
and leaves b
and c
unchanged.Your third example
Your third example, enhanced:
fn mutate_usize_one_time_mutable_pointer(mut n: usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = 48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let x = 42;
mutate_usize_one_time_mutable_pointer(x);
}
Let's go:
main
=> a fresh closet is allocated a: []
,let x = 42;
=> a: [42]
,mutate_usize_one_time_mutable_pointer
=> a fresh closet is allocated b: []
n: mut usize = x
=> b: [42]
,n = 48
=> modifies the value of n
: b: [48]
, note that a
is unchangedmutate_usize_one_time_mutable_pointer
, discards b
, a: [42]
.There is no pointer involved here, the name of the function is misleading.
Your final example
Your fourth example, augmented:
fn mutate_usize_one_time(mut n: &usize) {
println!("n before assigning in mutate_usize_one_time = {}", n);
n = &48;
println!("n after assigning in mutate_usize_one_time = {}", n);
}
fn main() {
let x = 42;
mutate_usize_one_time(&x);
}
Let's go:
main
=> a fresh closet is allocated a: []
,let x = 42;
=> a: [42]
,mutate_usize_one_time
=> a fresh closet is allocated b: []
,n: &size = &x
=> b: [0@a]
,48
=> b: [0@a, 48]
&48
=> b: [0@a, 48, 1@b]
n = &48
=> would lead to b: [1@b, 48]
however lifetimes forbid references pointing to something past them, this is forbidden.Hope it's starting making sense. If not... then go for a run, go out with friends, clear your head, read another explanation, clear your head again, and come back again. It'll seep in little by little ;)
Upvotes: 7