Rust what data hold a variable which has assigned a struct to it

Viewed 155

If we have the following code:

struct Person {
    age: i32
}

fn main() {
    let person =  Person{age : 52};
}

I know how to use the struct and all but what actually inside the person variable?

Is it a pointer to the first element of the struct? (Because all the elements are contiguous(?) so the compiler will know where all the elements are)

2 Answers

what actually inside the person variable?

The only thing "inside" is age, for a total of 4 bytes.

Is it a pointer to the first element of the struct?

No, it is not a pointer. It is just the value itself, which means it will be stored in the stack of the current frame/function, practically speaking (the compiler may remove it entirely from memory or not even use it, if the observable behavior does not change, but that is an optimization: conceptually, the variable is placed in the stack).

(Because all the elements are contiguous(?) so the compiler will know where all the elements are)

The compiler in most compiled languages always knows the exact layout of all the types (except special cases). That is the reason many optimizations can be done in those languages ahead of time. This information is normally discarded in system programming languages, which is why they usually do not support reflection and why it is quite hard to decompile them back into source code.

Looks like a pointer indeed.

EDIT: from the point of view of Rust, person contains the actual struct, so in the Rust code let person = Person{age : 52}; the variable person is not a pointer and doesn't contain a pointer. However, it could be implemented as a pointer, as the LLVM IR below shows. Thus, the Rust code could be translated to LLVM IR where %person will indeed be a pointer to the first element of the struct. Note that this IR can be optimised in such a way that the actual data could end up in a register, so not necessarily on the stack.

The LLVM IR for main looks like this:

; playground::main
; Function Attrs: nonlazybind uwtable
define internal void @_ZN10playground4main17h5b277f290810a924E() unnamed_addr #1 !dbg !315 {
start:
  %arg0.dbg.spill = alloca i32*, align 8
  %_11 = alloca i32*, align 8
  %_10 = alloca [1 x { i8*, i64* }], align 8
  %_3 = alloca %"std::fmt::Arguments", align 8
  %person = alloca i32, align 4
  call void @llvm.dbg.declare(metadata i32* %person, metadata !319, metadata !DIExpression()), !dbg !328
  store i32 52, i32* %person, align 4, !dbg !329
  // ...
}
  • %person = alloca i32, align 4 allocates space for an i32 on the stack and returns a pointer (so person is a pointer)
  • store i32 52, i32* %person, align 4 stores the integer 52 into that pointer. The code i32* %person says that %person is of type i32*, so again a pointer to an integer.

If you change the struct to look like struct Person { age: i32, thing: bool }, for example, the corresponding IR would be %person = alloca { i32, i8 }, align 4, so now it's a pointer to a struct of type { i32, i8 }.

Now storing the integer 52 would require some casting:

%0 = bitcast { i32, i8 }* %person to i32* // cast %person to i32*
store i32 52, i32* %0, align 4
Related