Hacker News new | ask | show | jobs
by munificent 5086 days ago
In C#, it's up to the person defining the type to decide whether it has value/stack semantics (a struct) or not (a class). Structs have some limitations regarding constructors, and being passed by value is surprising to users used to classes. For these and other reasons, the vast majority of types are classes.

So you can have on-stack or embedded objects, but they are fairly rare.

In Go, the user of a type decides whether it will be used by reference (on the GC heap) or directly on the stack or embedded in an object: you can take a pointer to any type, or use the type directly.

That gives the consumer of a type much greater flexibility to not create garbage if they don't want to.

2 comments

> In Go, the user of a type decides whether it will be used by reference (on the GC heap) or directly on the stack or embedded in an object: you can take a pointer to any type, or use the type directly.

Actually the distinction between heap and stack is not determined by how it is referenced. The compiler is free to stack-allocate any value as long as it does not escape the function. We can do this because pointers are opaque; there's no arithmetic.

The main point is that Go does not have classes. You just define methods on values. Values are no bigger than the data they represent, so you don't suffer from the same kinds of overheads seen in other "OOP"-centric languages.

How is that even possible? Surely there must be a type tag associated with each object if you can dispatch on it, just like in other languages.
A variable of an interface type is a pair consisting of a type descriptor and a concrete value. It is only when a concrete value of a statically known type is passed to something expecting an interface type that such a type tagged value is constructed.
For interface values there is a type associated with each value.

For normal values you don't need it, because Go is statically typed. The compiler knows where the method is.

Interesting, thanks!