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)

Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
  • 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. – user4815162342 Jan 16 '21 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. – user4815162342 Jan 16 '21 at 21:06

2 Answers2

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.

Acorn
  • 24,970
  • 5
  • 40
  • 69
-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
ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • 3
    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`. – user4815162342 Jan 16 '21 at 13:31
  • 3
    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". – user4815162342 Jan 16 '21 at 13:32
  • @user4815162342, well, in Rust, `person` contains the `Person` struct, but on this level of optimization `%person` in LLVM is of type `i32*`, which is a pointer. BTW, even for code like `let myvar = 5;`, LLVM will generate a spill, so `myvar` will end up being a pointer: `store i32 5, i32* %_myvar.dbg.spill, align 4`. However, in the actual machine code there may not be any distinction between `Person` and an integer. Indeed, it could very well end up in a register and thus may not be a pointer any more. – ForceBru Jan 16 '21 at 13:37
  • 4
    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. – user4815162342 Jan 16 '21 at 13:47
  • @user4815162342, I didn't say that "`person` contains a pointer", though. I said that in this IR, `%person` _is_ a pointer. I also specifically said that `alloca` allocates space _on the stack_. I don't see how this explanation may lead the OP to believe that `person` _contains_ a pointer and the actual data is allocated _on the heap_. It seems clear that LLVM IR is an implementation detail and that it can be optimised in various ways, so that the data may end up in a register or on the stack. Anyway, I'm new to Rust, so I could be wrong. – ForceBru Jan 16 '21 at 14:04
  • 2
    @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. – Acorn Jan 16 '21 at 14:15
  • 2
    *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++). – user4815162342 Jan 16 '21 at 14:23
  • 2
    *"I don't see how this explanation may lead the OP to believe that person contains a pointer and the actual data is allocated on the heap."* - you flat out say "Looks like a pointer indeed" while ignoring that LLVM IR is not indicative of that constitutes a value vs a pointer in the conventional sense. Saying that `person` *could* be a pointer is not a helpful description, *any* value stored in memory is accessed via its address, that's just the nature of the architecture. – kmdreko Jan 16 '21 at 19:25