Intresting how memory work for tuple.Please help

I am just a beginner in rust program, I was just trying to understand how data are stored in memory. Then I found a behavior for tuple. I couldn't understand why this is happening , please point me in right direction why this is happening??.

// main program
fn main() {
let tup: (i32, i32, u8) = (500, 2, 1);
mem(&tup); //same memory adress of "tup.0" ..ah fine predictable
mem(&tup.0);
mem(&tup.1);
mem(&tup.2);
println!();

//The variable "tup" points to adress of "tup.1" not "tup.0" Why???
//Is it because memory for "tup.1" than "tup.0" is larger??
let tup = (500, 6.11, 1);
mem(&tup); //same memory adress of "tup.1" ????
mem(&tup.0);
mem(&tup.1);
mem(&tup.2);
println!();

// If the above condition was true then why "tup" didnt point to "tup.2" in this scenerio???
let tup = (500, 1, 6.11);
mem(&tup); //same memory adress of "tup.0"
mem(&tup.0);
mem(&tup.1);
mem(&tup.2);
println!();

}
//function
fn mem(x: &T) {
let adress = format!("{:p} ", x);
println!("adress: {}", adress);
}

Output :
adress: 0x7fff9afb1738
adress: 0x7fff9afb1738
adress: 0x7fff9afb173c
adress: 0x7fff9afb1740

adress: 0x7fff9afb1778 //Why??
adress: 0x7fff9afb1780
adress: 0x7fff9afb1778
adress: 0x7fff9afb1784

adress: 0x7fff9afb17b8
adress: 0x7fff9afb17b8
adress: 0x7fff9afb17bc
adress: 0x7fff9afb17c0

The key point for search is probably "alignment". Fields of the struct can be reordered to ensure that:

  • they all are aligned according to their types (u32 needs to be at the multiple of four, for example);
  • there is the least possible amount of padding bytes.

Tuples are structs, too, so they are optimized similarly.

1 Like

Thanks for the replay this is understandable.

I also got more information on this topic from this link :
https://doc.rust-lang.org/reference/type-layout.html

I think the data in tuple are reordered based on their primitive type for better structure and to optimize the memory layout.

Be aware that there are no guarantees around the layout of most Rust types (though you can tell Rust to use C's layout instead). What you observe today may change tomorrow without warning.

With that caveat out of the way, let's take @Cerber-Ursi's advice and look at both the size and the alignment: Playground

Now we can make educated guesses at what's going on.

  • In the second tuple, it's moving the align-to-8 member to the top as that's the align of both .1 and the tuple as a whole. If it didn't reorder, it would need padding between .0 and .1, because .0 is only 4 bytes.
  • In the third tuple, the align situation is similar, but no reordering is needed to avoid padding because the sizes of fields .0 and .1 add up to 8 (so .2 will be aligned). This means that in the second tuple, it could have swapped .1 and .2 instead of .1 and .0.

The sizes of the three tuples are 12, 16, and 16. Next let's try with #[repr(C)], which will make the compiler keep fields in order.

  • Tuple 1 and Tuple 3 look the same
  • Tuple 2 is now 24 bytes -- 8 bytes larger

You can see from the offsets that there's 4 bytes of padding between .0 and .1 now. That's expected from the alignment considerations. Where are the other 4 bytes from? They're at the end, after .2, to ensure that the size of the tuple is a multiple of its alignment. (Though even this layout consideration may change some day!)

So now we see that by reordering the second tuple, the compiler made the size 8 bytes smaller.


Side note: Please read the pinned code formatting introduction.

6 Likes

Ah I think your guess is right maybe that is what going on behind the scene. And also thanks for code formatting introduction :heart: I was also looking for that.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.