Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Go forbid taking the address of (&) map member, yet allows (&) slice element?

Go doesn't allow taking the address of a map member:

// if I do this: p := &mm["abc"] // Syntax Error - cannot take the address of mm["abc"] 

The rationale is that if Go allows taking this address, when the map backstore grows or shinks, the address can become invalid, confusing the user.

But Go slice gets relocated when it outgrows its capacity, yet, Go allows us to take the address of a slice element:

 a := make([]Test, 5)  a[0] = Test{1, "dsfds"}  a[1] = Test{2, "sdfd"}  a[2] = Test{3, "dsf"}   addr1 := reflect.ValueOf(&a[2]).Pointer()  fmt.Println("Address of a[2]: ", addr1)   a = append(a, Test{4, "ssdf"})  addrx := reflect.ValueOf(&a[2]).Pointer()  fmt.Println("Address of a[2] After Append:", addrx)   // Note after append, the first address is invalid  Address of a[2]:  833358258224  Address of a[2] After Append: 833358266416 

Why is Go designed like this? What is special about taking address of slice element?

like image 607
NeoWang Avatar asked Sep 10 '15 07:09

NeoWang


People also ask

Why does Golang not have exceptions?

Why does Go not have exceptions? We believe that coupling exceptions to a control structure, as in the try-catch-finally idiom, results in convoluted code. It also tends to encourage programmers to label too many ordinary errors, such as failing to open a file, as exceptional.

Should I turn off prevent cross site tracking?

Evidently, switching cross-site tracking off exposes you to some serious stalking across multiple sites. If you value your privacy, you'd be wise to leave all tracking prevention tools firmly in the on position.

What does it mean when it says Safari has prevented trackers from profiling you?

Safari employs a feature called Intelligent Tracking Prevention which uses machine learning to determine which websites can track you across the internet. The browser will block and delete third-party trackers from sites you haven't visited over the last 30 days.

How do you use God forbid?

Definition of God/heaven forbid —used in speech to say that one hopes a bad thing will not happen This is the number you should call if, God forbid, you should get into an accident. Heaven forbid that something bad should happen.


2 Answers

There is a major difference between slices and maps: Slices are backed by a backing array and maps are not.

If a map grows or shrinks a potential pointer to a map element may become a dangling pointer pointing into nowhere (uninitialised memory). The problem here is not "confusion of the user" but that it would break a major design element of Go: No dangling pointers.

If a slice runs out of capacity a new, larger backing array is created and the old backing array is copied into the new; and the old backing array remains existing. Thus any pointers obtained from the "ungrown" slice pointing into the old backing array are still valid pointers to valid memory.

If you have a slice still pointing to the old backing array (e.g. because you made a copy of the slice before growing the slice beyond its capacity) you still access the old backing array. This has less to do with pointers of slice elements, but slices being views into arrays and the arrays being copied during slice growth.

Note that there is no "reducing the backing array of a slice" during slice shrinkage.

like image 137
Volker Avatar answered Sep 20 '22 00:09

Volker


A fundamental difference between map and slice is that a map is a dynamic data structure that moves the values that it contains as it grows. The specific implementation of Go map may even grow incrementally, a little bit during insert and delete operations until all values are moved to a bigger memory structure. So you may delete a value and suddenly another value may move. A slice on the other hand is just an interface/pointer to a subarray. A slice never grows. The append function may copy a slice into another slice with more capacity, but it leaves the old slice intact and is also a function instead of just an indexing operator.

In the words of the map implementor himself:

https://www.youtube.com/watch?v=Tl7mi9QmLns&feature=youtu.be&t=21m45s "It interferes with this growing procedure, so if I take the address of some entry in the bucket, and then I keep that entry around for a long time and in the meantime the map grows, then all of a sudden that pointer points to an old bucket and not a new bucket and that pointer is now invalid, so it's hard to provide the ability to take the address of a value in a map, without constraining how grow works... C++ grows in a different way, so you can take the address of a bucket"

So, even though &m[x] could have been allowed and would be useful for short-lived operations (do a modification to the value and then not use that pointer again), and in fact the map internally does that, I think the language designers/implementors chose to be on the safe side with map, not allowing &m[x] in order to avoid subtle bugs with programs that might keep the pointer for a long time without realizing then it would point to different data than the programmer thought.

See also Why doesn't Go allow taking the address of map value? for related comments.

like image 33
user13097 Avatar answered Sep 23 '22 00:09

user13097