Can anyone explain, in Go programming language are values stored in the Stack? - go

I got the Example struct and where my is object allocated?
type Example struct {
field_a int
}
var ex Example = Example{10} // Stack's object or Heap ?
var ex Example = new(Example{10}) // Stack's object or Heap ?

Unlike C or C++, you don't know, golang abstracts the memory allocation.
Moreover, you cannot choose where the memory is allocated.

Related

Is it safe to directly convert a struct 'point' to another struct using unsafe.Pointer?

Is it safe?
(*TeamData)(unsafe.Pointer(&team.Id))
Example code:
func testTrans() []*TeamData {
teams := createTeams()
teamDatas := make([]*TeamData, 0, len(teams))
for _, team := range teams {
// is this safe?
teamDatas = append(teamDatas, (*TeamData)(unsafe.Pointer(&team.Id)))
}
return teamDatas
}
// ??
teams := testTrans()
Will the members of the teams := testTrans() array be garbage collected?
There are many structs and many fields returned through grpc and their definitions are the same as the local definitions, so I want to use this more efficient way((*TeamData)(unsafe.Pointer(&team.Id))), but I don't know if there will be any risks.
Full Example:
https://go.dev/play/p/q3gwp2mERvj
The documentation for unsafe.Pointer describes supported uses. In particular:
(1) Conversion of a *T1 to Pointer to *T2.
Provided that T2 is no larger than T1 and that the two share an
equivalent memory layout, this conversion allows reinterpreting data
of one type as data of another type.
Go's garbage collector recognises interior pointers an will not collect the original allocation until there are no remaining references to that block.
Hence the larger allocation (GrpcRetTeam in your example) will be pinned while references to *TeamData exists.
Another critical consideration is the alignment of the struct fields. Eg:
type Parent struct {
A uint8
B uint8
// 6 bytes of padding to align C.
C uint64
}
type Bad struct {
B uint8
// 7 bytes of padding to align C.
C uint64
}
In this case it would be invalid to use unsafe to extract Bad from Parent since the memory layout is different.
In most cases it's typically better to avoid unsafe.Pointer tricks unless required to meet functionality or performance requirements. It's often possible to refactor code to minimise allocations instead.
If you must use unsafe to meet performance requirements --
I would recommend implementing a test using the reflect package to ensure the memory alignment/layout is valid for the child struct.

Struct mutation & memory management

I know structs are made for the purpose of being immutable but I don’t quietly understand the concept of “when we change a property in struct, the whole struct gets destroyed and created again” so now when we have a simple struct like:
`
enter code here`struct MyStruct { var num: Int }
var sample = MyStruct(num: 1)
var sample2 = sample
sample.num.address (unsafePointer) // address: 0x0000678ed8
Now if we change sample’s num property,
sanple.num = 2
Will its address in memory change? Or the struct just gets recreated at the ‘same’ address in memory? I’m really confused cause the address of properties do not change!!!

Is `String::with_capacity()` equal to `malloc`?

I read this article a few days ago and I thought what is the best way to implement such a thing in Rust. The article suggests to use a buffer instead of printing the string after each iteration.
Is this correct to say String::with_capacity() (or Vec) is equal to malloc in C?
Example from the codes:
String::with_capacity(size * 4096)
equal to:
char *buf = malloc(size * 4096);
It is not "equal", Rust's String is a composite object; String::with_capacity creates a String which is not only a buffer; it is a wrapper around a Vec<u8>:
pub struct String {
vec: Vec<u8>,
}
And a Vec is not just a section in memory - it also contains a RawVec and its length:
pub struct Vec<T> {
buf: RawVec<T>,
len: usize,
}
And a RawVec is not a primitive either:
pub struct RawVec<T> {
ptr: Unique<T>,
cap: usize,
}
So when you call String::with_capacity:
pub fn with_capacity(capacity: usize) -> String {
String { vec: Vec::with_capacity(capacity) }
}
You are doing much more than just reserving a section of memory.
That isn't quite accurate. It'd make more sense to say String::with_capacity is similar to std::string::reserve. From the documentation:
Creates a new empty String with a particular capacity.
Strings have an internal buffer to hold their data. The capacity is
the length of that buffer, and can be queried with the capacity
method. This method creates an empty String, but one with an initial
buffer that can hold capacity bytes. This is useful when you may be
appending a bunch of data to the String, reducing the number of
reallocations it needs to do.
If the given capacity is 0, no allocation will occur, and this method
is identical to the new method.
Whether or not it uses something similar to malloc for managing the internal buffer is an implementation detail.
In response to your edit:
You are explicitly allocating memory, whereas in C++ a memory allocation for std::string::reserve only occurs if the argument passed to reserve is greater than the existing capacity. Note that Rust's String does have a reserve method, but C++'s string does not have a with_capacity equivalent .
Two things:
If you link to an allocator, well, just call malloc.
The hook into the default global allocator is still unstable, but if you're on nightly, you can call it directly.
On stable Rust today, the closest thing you can get is Vec if you want to use the global allocator, but it's not equivalent for reasons spelled out in other answers.

map[T]struct{} and map[T]bool in golang

What's the difference? Is map[T]bool optimized to map[T]struct{}? Which is the best practice in Go?
Perhaps the best reason to use map[T]struct{} is that you don't have to answer the question "what does it mean if the value is false"?
From "The Go Programming Language":
The struct type with no fields is called the empty struct, written
struct{}. It has size zero and carries no information but may be
useful nonetheless. Some Go programmers use it instead of bool as the
value type of a map that represents a set, to emphasize that only the
keys are significant, but the space saving is marginal and the syntax
more cumbersome, so we generally avoid it.
If you use bool testing for presence in the "set" is slightly nicer since you can just say:
if mySet["something"] {
/* .. */
}
Difference is in memory requirements. Under the bonnet empty struct is not a pointer but a special value to save memory.
An empty struct is a struct type like any other. All the properties you are used to with normal structs apply equally to the empty struct. You can declare an array of structs{}s, but they of course consume no storage.
var x [100]struct{}
fmt.Println(unsafe.Sizeof(x)) // prints 0
If empty structs hold no data, it is not possible to determine if two struct{} values are different.
Considering the above statements it means that we may use them as method receivers.
type S struct{}
func (s *S) addr() { fmt.Printf("%p\n", s) }
func main() {
var a, b S
a.addr() // 0x1beeb0
b.addr() // 0x1beeb0
}

Difference between initialization and zeroing, and new() and make() in GoLang

I am coming from C# background, and I am little confused with the way of GoLang initialization and zeroing definitions. I think you can guess that this confusion arises from make() and new() functions in Go. What should I expect happening internally when these methods run? What happens when initialization and zeroing happens?
I know that there is an init() function in GoLang that is used to initialize packages. But I think it is different than this.
Anyways, what is the difference between them?
Update
I answered my own question, please check it to see my answer.
I think I have figured it and decided to share what I figured so far.
make() vs. new()
I think I now understand the difference between make() and new(). At first, it was little confusing, but here what I got:
new is simply like new in C# or Java, but since there is no constructor in Go, all the fields (like in Java and C# terminology) will be zeroed. Zeroing means more like defaulting the fields. So if the field type is int, then it will be 0, or if it is a struct, then it will be defaulted to nil, and "" for string types. It is actually similar to C# and Java when there is only parameterless constructor available and you are not setting the members to something else manually.
However, types like map, slice, and channels are different. They are different because they are actually wrapper types that wrap an array type to hold the values behind the scenes. So something like List<T> or ArrayList in C# and Java. But using new is not enough in this situation, because the underlying array should be initialized to an empty array to be usable. Because you cannot add or remove from a field of type array which is nil (or null). Therefore, they provided a make() method to help you to initialize slices and such.
So what happens when you use new() over slices, for instance? Simple: Since the underlying array will be nil, the slice will be pointing at a nil array.
So new() would look like the following C#/Java code:
public class Person{
public string Name;
public int Age;
public Address HomeAddress;
}
var person = new Person();
Console.WriteLine(person.Name); // ""
Console.WriteLine(person.Age); // 0
Console.WriteLine(person.HomeAddress); // null
make(), on the other hand, would look like this for slice,map, and channels:
public class PersonList{
// We are initializing the array so that we can use it.
// Its capacity can increase.
private Person[] _personList = new Person[100];
public void Add(Person p){}
public void Remove(Person p){}
public Person Get(int index){}
}
Initialization vs. Zeroing
Simply speaking, zeroing is a form of initialization. At first, I thought they were different but they are not. Initialization is a more general term, whereas if you are set the fields (properties, etc.) of a struct or a variable to its type default such as 0, nil, "", false, etc., then this is called zeroing. However, you can, for instance, use Composite Literals like hello := Hello{name="world"}, which is similar to var hello = new Hello() {Name = "World"} in C#, then you initialize your Hello object with a name field set to world.
In C#, at the time you say new List<string>(), [the underlying array field is initialized to a new array], and make) is performing a similar operation behind the scenes but as a language construct (built in the language itself):
(http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646):
So new does zeroing and returns a pointer back. Whereas make() initializes to underlying array to an array with default values for each element and returns the value itself rather than a pointer.
new returns a pointer to the type, and it's the only way to return a pointer to a native type in one step (aka int, float, complex):
intPtr := new(int)
// or
var i int
intPtr := &i
make is for initializing channels, slices and maps.
All variables are zeroed on creation, regardless of how you create them.
The spec about make: https://golang.org/ref/spec#Making_slices_maps_and_channels
The spec about zero value: https://golang.org/ref/spec#The_zero_value
new(T) allocates zeroed storage for a new item of type T and returns its address, a value of type *T: it returns a pointer to a newly allocated zero value of type T, ready for use; it applies to value types like arrays and structs. It is
equivalent to &T{ }
make(T) returns an initialized value of type T; it applies only to the 3 built-in
reference types: slices, maps, and channels.
var p *[]int = new([]int) // *p == nil; with len and cap 0
//or
p := new([]int)
var v []int = make([]int, 10, 50)
//or
v := make([]int, 10, 50)
//This allocates an array of 50 ints and then creates a slice v with length 10 and capacity 50 pointing
//to the first 10 elements of the array

Resources