4 ms·
Yes. That is correct. Here is an interesting piece of code. This will work without an error. package main import ( "fmt" ) func main(
by andreygrehov 6y ago
Yes. That is correct. Here is an interesting piece of code. This will work without an error.
package main
import (
"fmt"
)
func main() {
foo := NewFoo()
foo.sayHello()
}
type Foo struct {
}
func NewFoo() *Foo {
return nil
}
func (f *Foo) sayHello() {
fmt.Println("Hello, World")
}
In Go, the function to be called by the Expression.Name() syntax is entirely determined by the type of Expression and not by the particular run-time value of that expression, including nil.
- deleted 6y ago[deleted]
- cmckn 6y agoThat fact that calling a function on a type does not inherently dereference the pointer seems...nuts. Do gophers routinely check if the pointer is `nil`? If the function does not utilize the pointer (like your example) and so avoids an error, the function probably doesn't need to be defined on the type in the first place. This seems like a bug-prone rough edge to me, coming from Java.
- kitd 6y agoThat fact that calling a function on a type does not inherently dereference the pointer seems...nuts. Only if you come from a vtable-based OO language. Go isn't one, so while it looks unconventional, it is rational.
- kroltan 6y agoOn one hand, it is rational if you think of Go functions "on a type" as just having an extra argument, but then one might ask, why bother with the special syntax at all? Just go the C way and if you want to take a "self", actually take it as a regular parameter. That is: (sorry if the syntax is wrong, I have never used Go) func (f *Foo) sayHello() { ... } Would become func sayHello(f *Foo) { ... } Heck, if you want to keep the value.sayHello() syntax, you can, which would still allow you to build fluent interfaces or whatnot, with the bonus point of being UFCS!
- AlexCoventry 6y ago> why bother with the special syntax at all? It dovetails nicely with interfaces.
- throwaway34241 6y agoThey basically are functions with an additional argument. You can call them with the method syntax, but you can also call them like a regular function. To use your first example, you can also call it like this: (*Foo).sayHello(f) // the same as f.sayHello() It's mainly useful when you want to get a reference to the method itself: f := (*Foo).sayHello // a function that takes one arg of type *Foo For most purposes the two are the same, the main difference is with with how interfaces work in Go. Functions declared with the special syntax can be used to satisfy interfaces, which in Go means keeping some run-time type information around.
- andreygrehov 6y agoSome people do exactly that and avoid methods altogether. It's perfectly valid to use: func sayHello(f *Foo) { ... } One major difference between functions and methods in Go is you can have multiple methods with same name while no two functions with the same name can be defined in a package. Also, there is a subtle difference, which is more subjective, between user.IsActive() and IsActive(user). The former explicitly implies that you operate on a user, whereas the later one isn't that obvious, like what if I do IsActive(group), is that also allowed? I like that Go is not trying to be super flexible, but it also nicely holds a niche of a slightly improved (in terms of usability) C.
- andreygrehov 6y agoYea, you are correct in your understanding, but such code is super rare in my experience. In fact, until recently I didn't even know such behavior exists at all. Answering your question, checking if a pointer is `nil` isn't that common among gophers. A more common idiom is to return both a pointer and an error, and then check if error is not `nil`: user, err := users.FindByID(id) if err != nil { return nil, err } If the return value is just a pointer, in my books a nil value should be avoided. I guess what's not cool here is that there are no guarantees. If I'm using a library in which a function returns just a pointer, I sometimes jump in the function's body to check if it ever returns nil so that I don't have to pollute my code with `if` statements. Like, you almost never check for `nil` when dealing with simple factories, eg foo := NewFoo(). However, when it comes to a more complex method calls, it might be safer to add a quick `nil` check and forget about it.
- foobiekr 6y agoThis property is actually great for making chains of calls where any of the intermediate objects may be missing, allowing you to short circuit. x = A().B().C().D().E().F() for example. Very useful when dealing with big complex trees.