3

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
  • 2
    There is no pointer: Person holds the actual number, and so does the person variable. For examplel, you could take the address of person, dump it, and find that the number 52 is stored there, not some pointer leading to it. Commented Jan 16, 2021 at 13:34
  • In comparison, in the following declarations: let person1 = &Person { ... }; let person2 = Box::new(Person { ... }); let person3 = Rc::new(Person { ... }) every person* variable does contain a pointer. Commented Jan 16, 2021 at 21:06

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

Comments

-3

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

8 Comments

This answer is misleading: the person variable does not contain a pointer, it contains the Person struct, i.e. an actual number. Where the number is stored depends on the optimization level - on higher optimization levels it might well end up in a register, or be completely eliminated. The alloca instruction and the resulting pointer are an artifact of the intermediate representation employed by the compiler. The final representation, the actual machine code, is likely not to distinguish between Person and an i32.
While one could argue that person, when stored on the stack, is in some sense a pointer because the processor must dereference the stack pointer to access it, the same could then be said of an i32 as well, and no one would normally claim that an i32 variable somehow holds a "pointer".
LLVM IR is an implementation detail of the compiler. It's incorrect to say that person "contains a pointer" because it leads the OP to think that Rust structs are implemented like Java or Python objects, where a variable truly contains a reference to a heap-allocated object. Rust is clear in that person contains the actual data: you can take the address of person and dump it to find the number 52 right there, not a pointer. The CPU might use the stack pointer to access that data, but that applies to any data that's not in a register and is not a property of variables.
@ForceBru The question is not about how the LLVM IR represents objects in the stack or how typical architectures use a stack register and offset addressing. Even without any optimization, the variable on its own will never be a pointer.
It seems clear that LLVM IR is an implementation detail - That's not at all clear from your answer (prior to the edit), which gives a completely opposite impression and incorrectly representers the semantics of rust's variables. Your answer clearly says that person contains a pointer, which is patently false in Rust (and other languages with the same memory model, like C and C++).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.