0

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:

  1. index must refer to its updated value => mut index
  2. index must be capable of changing the referred value and pass mutable references to the other functions. => &mut usize
  3. If it were another function parameter with the same type (mut index2: &mut usize), the compiler would not let me have 2 mutable references pointing to the same memory location.
Community
  • 1
  • 1
freinn
  • 1,049
  • 5
  • 14
  • 23
  • What are you trying to do? Modify the value that the reference point to or change the reference itself? – Tatsuyuki Ishi Mar 11 '17 at 11:23
  • @TatsuyukiIshi edited with more complete information about my goals – freinn Mar 11 '17 at 11:32
  • @freinn I guess you are still confused. Concentrate, by then try to recall your thoughts and dig into them. Before you understand a code you shall be able to guess what it does in a first glance. We can't help you always, but we can guess. From you write we see what think and where you think. – Vitali Pom Mar 27 '17 at 14:02
  • @VitaliPom question == question => not knowing. answer == answer => knowledge is there. – freinn Mar 28 '17 at 11:16

3 Answers3

6

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:

  • a function frame is a closet, with lots of drawers,
  • each drawer may contain one value.

For the moment, let's forget about types, stack and heap: we only have closets and drawers:

  • a value is in a drawer, it may moved or copied to another drawer,
  • a reference is the address of this value, represented as a pair of IDs (closet ID, drawer ID).

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?

  • call 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?

  • call main => a fresh closet is allocated a: [],
  • let mut x = 24; => a: [24],
  • call mutate_usize_again => a fresh closet allocated b: [],
  • with &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!

  • call main => a fresh closet is allocated a: [],
  • let mut x = 24 => a: [24],
  • call mutate_usize_two_times => a fresh closet is allocated b: [],
  • with n: &mut usize = &mut x => b: [0@a],
  • *n = 8 => stores 8 in the drawer pointed to by n: a: [8], b unchanged,
  • call mutate_usize_again => a fresh closet is allocated c: [],
  • with 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,
  • let's pretend you used 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:

  • before call, we had: a: [8], b: [0@a]
  • call mutate_usize_again => a fresh closet is allocated c: [],
  • with 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:

  • call main => a fresh closet is allocated a: [],
  • let x = 42; => a: [42],
  • call mutate_usize_one_time_mutable_pointer => a fresh closet is allocated b: []
  • with n: mut usize = x => b: [42],
  • n = 48 => modifies the value of n: b: [48], note that a is unchanged
  • end of mutate_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:

  • call main => a fresh closet is allocated a: [],
  • let x = 42; => a: [42],
  • call 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 ;)

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
2

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);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Tatsuyuki Ishi
  • 3,883
  • 3
  • 29
  • 41
-5

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vitali Pom
  • 602
  • 1
  • 8
  • 29
  • Yayyyy glad to help ^^ – Vitali Pom Mar 11 '17 at 12:14
  • A slash is `/` (and a backslash is ``\``). What symbol do you actually mean? – Shepmaster Mar 11 '17 at 15:21
  • @Shepmaster backslash – Vitali Pom Mar 11 '17 at 16:23
  • That leaves me even more confused. There are no backslashes *anywhere* in this code. I really don't know what you are trying to say. – Shepmaster Mar 11 '17 at 19:17
  • @Shepmaster I just tried to dig into the editor's head. He was asking how pointers work. Before my comment, unless I confuse the Q, he verified and later deleted his comment, about my answer - that I hit his thoughts. – Vitali Pom Mar 15 '17 at 13:29
  • My point is that the backslash symbol (``\``) has *nothing to do* with pointers. I still don't know why you brought it up in the first place. – Shepmaster Mar 15 '17 at 13:32
  • Ooooh oh ooooh sorry. I'm mistaken with the other slash. Thanks for correcting :) – Vitali Pom Mar 15 '17 at 13:36
  • I truly apologize, as I don't think I'm doing a good job of communicating. **Neither** slash (`/`) nor backslash (``\``) have anything to do with pointers. – Shepmaster Mar 15 '17 at 13:39
  • In physics I think, there is a reference - "Even mistake is a truth sentence". He was asking about references and I answered in his language :D with backslash instead of slash.... it's psychological mistake :)))))) it's truth, he confirmed/ – Vitali Pom Mar 15 '17 at 14:39
  • @Shepmaster speak in the language children speak, they'll get you. Others not. – Vitali Pom Mar 15 '17 at 14:40
  • 1
    Please understand that answers are also made to be understood by other people who stumble upon the same or similar problems. As it is, this answer is of poor quality and hardly makes any sense to the wider Rust programming community. In fact, nothing so far even shows that the OP understands it. – E_net4 Mar 15 '17 at 17:49
  • @E_net4 They deleted my comment because this is not a good answer. I only tried to be grateful. The only thing he's saying is that it must be an even number of & and *, like in C++ etc. But he is confusing character's names or thinking in another language. – freinn Mar 15 '17 at 18:53
  • yeah I know, they have their own distinctions in their heads :) it's what I thought for the first time. It's okay, I hope it gave you boost. Good luck in the next steps :)) @freinn – Vitali Pom Mar 15 '17 at 19:02
  • @E_net4 there are 80% of children or new scholars who learn how to code here, we have a different language of speaking before reaching the point that "Pointer" and "Reference" are two different tings. let us, it will be rewarded in the end for your. 80% of the users here are going to made 4x working power in the next 10 years for the market and double the working power already in the next 2 years. Consider us as the people who write the next gen. of tools for you, we can also revenge you back ;) but you're okay. I'm glad people started to arise attention on those important topics. – Vitali Pom Mar 15 '17 at 19:08