5 ms·
I am still not sure why the language creators of Go decided to make pointers explicit (syntactically) instead of making references the default like most other
by agentgt 9y ago
I am still not sure why the language creators of Go decided to make pointers explicit (syntactically) instead of making references the default like most other languages. That is you still have pointers but you don't have to put "*" all over the place. I suppose it is because the language designers came from C or maybe they wanted to be that explicit?
I understand the value of having pointers even in a GC but I'am actually more concerned with the resource I'm pointing to and its lifecycle than that is pointers. That is there should be different types of pointers depending on where the data is stored and how it is reclaimed (something Rust does nicely with generics).
I'm not trying to bash Go rather I must be missing something (I don't know the language that well).
- omginternets 9y agoI always assumed it was because passing by copy was useful in highly concurrrent environments. It's essentially pseudo-immutability, if you will.
- steveklabnik 9y agoRust does not use references by default either, so I wonder why you don't mind it there but mind it in Go.
- agentgt 9y agoBecause Rust is not a GC language. I should have perhaps explained that better. My round about point mentioning Rust was that instead of putting "*" all over the place you could just have types that represent such (pointers) or the opposite (ie value types) depending on what the language defaults to. I mean the pointer in some senses is effectively abstract wise Pointer<SomeType>. However I suppose this is difficult with out generics. That is I don't mind the explicitness of Rust because I guess I expect it just like C/C++ but I probably incorrectly expect it with Go lang. This is mostly because I come from a JVM background where I expect the VM to figure out what is more optimal and consequently also have very little experience using in/out parameters.
- mixedCase 9y agoIt's not so much about what's optimal for performance but as to which piece of state changes and which one doesn't. Implicit pointers means you have to often think extra when shuffling data structures around.
- agentgt 9y ago> Implicit pointers means you have to often think extra when shuffling data structures around. This basically answered my question. Thank you. I don't know why I didn't make that connection.
- robzyb 9y agoI didn't know the answer, but I just did a bit of Googling to determine that one of the reasons that pass-by-value can be preferred is because it makes life easier for the garbage collector. See golang escape analysis: http://www.agardner.me/golang/garbage/collection/gc/escape/analysis/2015/10/18/go-escape-analysis.html http://www.agardner.me/golang/garbage/collection/gc/escape/a...
- echlebek 9y agoHaving explicit pointers gives you more control over allocation. As you mention, Go is a GC language, and there are no guarantees of whether allocation will occur on the stack or the heap. But generally, non-pointer values will be stack-allocated. This reduces pressure on the GC, since the values are just discarded along with the stack frame on return. For this reason, it's usually considered good form to pass values to functions instead of pointers, even if the values are larger. This allows the compiler to prove through escape analysis that the arguments don't need to be heap-allocated. In some cases pointers can be proven not to escape, but in a limited set of circumstances. Having explicit pointers also resolves type system awkwardness between simple and complex data types. Consider Java, which is also a pass-by-value language, but has "reference" types, except for int, float, etc. So everything is a reference except this bag of simple data types, effectively creating a 2-tiered type system. Java has dealt with this by implementing boxing and unboxing rules for int/Integer and friends, which is fine, but adds complexity. Finally, one of my personal favourite features about having a complex data type that is a value instead of a pointer or reference is that you can use structs and arrays as map keys.
- agentgt 9y agoAs I mentioned in another similar comment I think this is the key point for me why it makes sense: > Having explicit pointers also resolves type system awkwardness between simple and complex data types. That is it appears to be worth it to have explicit pointers for data structures (and the manipulation of those data structures) and probably more so with a concurrent language. That is when the programmer sees things that don't have pointers they can make some generally assumptions (the struct or whatever is generally immutable).
- kristianp 9y agoSo how big does my struct need to be before its more efficient to pass a pointer to it to a function?
- Matt3o12_ 9y agoI've often wondered this myself. Perhaps the right answer would be to benchmark and/or use the profiler. On the other handy, I have never really had a situation where I had to pass a strict that was really big (more then 1MB). Most of the time, big data structures are actually just slices, which use pointers to point to the memory address of the underlying array (as far as I understand). So having sich big struct is rather unusual. Maybe it is possible to do with strings, though it would depend on how are strings implemented in go (I would guess they are just slices as well). Furthermore, having big strings 1<x<50 MB) is rather unusual unless the strings can also become really big (1GB in which case you should not probably pass-by-reference or even better, buffer the string because arbitrary length strings can easily cause ram problems).
- pokstad 9y agoA pointer tells you that the underlying data type can be modified in place. Pass by value ensures immutability to the original value. The exception to the syntax are collections that use `make` like slices, maps, channels.
- agentgt 9y agoYes I understand what the pointer means I'm just surprised they would default to that or generally encourage it (again I don't know the language that well but it appears pass by value is not used often for I guess performance reasons). I probably just haven't looked at the right libraries or programming practices in Golang but it appears most APIs are very much mutable and thus use pointers. With Java (and lets assume Java now has Value types for argument sake) the default is immutable references but the language could easily provide other reference types (and in fact sort of does) that would allow pointer like behavior and value like behavior. And with immutable references the runtime may actually copy anyway for some types (I vaguely remember the JVM doing this somewhere). The reason I don't mind pointers in Rust or C/C++ is that I expect to manage the memory because the languages doesn't do the memory management.
- pokstad 9y agoThe language doesn't really have a preference for either. When you see it being done mostly with pointers that's usually the author's preference. Also, using a pointer for a receiver is more convenient than pass by value since you can always derive the value by dereferencing a pointer, but you cannot derive the original pointer after a value has been passed by value (since the address has changed during the copy operation).
- abtinf 9y agoI love Go and it is my preferred language. But the pointer thing can be very confusing when it comes to slices. Idiomatic go is to generally use slices, not arrays. Slices are passed by copy, but contain a reference to the underlying array. In other words, the default is pass by copy, but the fundamental idiomatic data structure is effectively pass by reference. This makes it way too easy to accidently pass parameters by reference when you didn't intend to or, even worse, try to manipulate the same array from multiple goroutines without proper locking. This bit me hard in an early system I wrote in Go. The situation is marginally improved with the panic-on-multiple-thread-manipulating-slice feature. The only way to deal with it is to be paranoid about any code that passes slices, which weakens one of my favorite things about Go: you can look at the code, instantly understand what it is doing, and trust there is no magic. Edit: to be clear, I think pointers should be included in Go. I would just prefer that slices be handled differently - perhaps by treating them as a special case, where the compiler passes the underlying array by copy unless explicitly told to pass by reference.
- Animats 9y agoThis makes it way too easy to accidently pass parameters by reference when you didn't intend to or, even worse, try to manipulate the same array from multiple goroutines without proper locking. That's the biggest hole in Go's "share by communicating" story. In practice, Go channels are just a built-in queue object, with the same concurrency problems as queue objects in other languages. Rust has a borrow checker, so if you pass a reference through a queue, the compiler notices. Gp strings are read-only, so slices of strings, although sharing data, are not sharing mutable data. That tends to make the problem at least manageable.
- yandrypozo 9y agoThat's a common mistake, don't look at channels as queues, one thing is be buffered and other one it's working as a queue, actually buffered channels should be avoided at least you really need a buffer.
- Animats 9y ago
- brandonbloom 9y agoI think others answered the question of pointers vs not-pointers reasonably well, but your question implies you already understood that. The interesting thing to me is that they _did_ make some pointer types implicit: maps, chans, etc. Moreover, you can easily hide a pointer in a struct, such that the use site gives no hint as to whether or not you're going to have "spooky action at a distance" for mutations. Given this, a more interesting question is "Why can't I define types that are implicitly always pointers?" I don't know the answer to that. My guess would be path-dependent design. As I understand it, even maps and chans were explicitly pointers for a long while in the early history of Go. I've found that I can classify all structs in to two groups: 1) simple record values with all public fields; rare methods for convenience or specific polymorphism. 2) encapsulated machines with all private fields; only accessed through rich method interface. Any blending of public and private fields has turned out to be a mistake in my experience. For record types, the implicit copying is valuable for passing data around. With encapsulated machines, I almost always want all usages to be a pointer. I wish I could define types like this: type MyMachine *struct { ...private fields... } Which would basically compile like this: type machineState struct { ...public fields... } type MyMachine struct { *machineState } One problem with this approximate macro-expansion is that you can't use "nil" for MyMachine, instead you need to use (MyMachine{}) as a zero value. I'd change that across the language too and just make nil a proper polymorphic literal zero value for any type.
- doubleorseven 9y agoThis might be off subject but to me this language always tries to make me understand the underlying of being a programmer. I always look at the third party support for versioning as a way of the Golang to tell me: "this is one way of doing it. Read it, copy what you need and make it your own". +1 for the javascript breakouts every now and then.
- gshulegaard 9y ago> instead of making references the default like most other languages My primary language (at the moment) doesn't make references the default and actually doesn't standardize allocation practice. This can be very confusing and will often lead to unexpected results unless you are aware enough of the nature of the language to avoid common pitfalls. To me, this adds to the learning curve and is an unnecessary headache. I'm not saying this means that explicit pointers are the "right" way of doing things...but just providing an example where a language without explicit pointers can result in odd behavior. Also, I am not saying my primary language is bad...it is, after all, my primary language so I continue to use it quite extensively. Just trying to give some food for thought since you posed a good question.
- jug 9y agoI think there's a good case to be made for being explicit in Go although it may feel like a step backwards when Go has a GC. In C# where pointers are implicit, if you need to pass function parameters by reference you need to prefix them with the `ref` or `out` keywords depending on if they are supposed to be read-write or write-only. In Java, you need to wrap them with objects. Then comes the arrays. In C#, they are references passed by value by default. Not references passed by reference, which still needs the `ref` keyword. So array content changes will be seen by the caller by default, but not full reassignments. I think it can easily start to get confusing when you start to think about the details in what the language does under the hood, and all that only because of trying to steer clear from an innocent * character here and there. A character that also serves to inform and remind readers of the code what is going on, what the intent of the code is. And again, it's not as if other similar GC'ed languages can avoid this either. There always comes a point where the compiler must know. In Go's case I think the designers just chose the path to second guess less and just leave it to the user for less compiler magic.