Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang: Where is an Interface method called?

Tags:

go

I don't understand at which point an Interface method is being called. I'm looking at the following example from the Go Tour:

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
    a := Person{"Arthur Dent", 42}
    z := Person{"Zaphod Beeblebrox", 9001}
    fmt.Println(a, z)
}

Problem:

I understand that the func (p Person) receives the String() method and that it returns the string I want to display. But the fmt.Println in the main() method has to call String() at some point, right?

I had a look at the source of fmt in godoc, but I still cannot figure it out!

Another example:

If I add my own interface, lets say Stringer2 with a method called String2() and then create a func (p Person) String2() {....}. How does String() get executed by fmt.Println, but String2() not?

like image 678
Kosie Avatar asked Dec 11 '15 17:12

Kosie


People also ask

What does interface {} mean in Golang?

interface{} means you can put value of any type, including your own custom type. All types in Go satisfy an empty interface ( interface{} is an empty interface). In your example, Msg field can have value of any type.

How do I instantiate an interface in Golang?

How to Create an Interface? In Go, you can create an interface using the type keyword, followed by the name of the interface and the keyword interface. And, you can specify method signatures inside curly braces. You will get to understand the creation of interfaces with examples.

Is interface a reference type in Golang?

map, pointer and slice types are reference types in Go. map, pointer, slice, channel, function and interface types are reference types.


2 Answers

The value is passed to Println as an interface{}, and is checked if it satisfies the fmt.Stringer interface via a "type assertion" often in the form of a "type switch".

func IsStringer(i interface{}) {
    switch s := i.(type) {
    case fmt.Stringer:
        fmt.Println("Person a has a String() method")
        fmt.Println(s.String())
    default:
        fmt.Println("not a stringer")
    }

    // OR for a single type

    if s, ok := i.(fmt.Stringer); ok {
        fmt.Println("Person a has a String() method")
        fmt.Println(s.String())
    }
}

However, other methods may take precedence when printing from the fmt package. There are first checks for fmt.Formatter, fmt.GoStringer, error, and then finally fmt.Stringer.

like image 178
JimB Avatar answered Oct 23 '22 07:10

JimB


The fmt package works with the interfaces it defines, in this case Stringer. It does not know of interfaces defined by you so it wouldn't know to call String2() even if you pass it a type that meets the Stringer2 interface.

Interfaces are a way to have common behavior between types. So if you create a function Foo(s Stringer2), Foo can simply call s.String2() confident that anything passed into it will have the function String2().

To go a bit deeper, fmt.Println takes interface{} types and then uses reflection to check if the given argument meets the Stringer interface to then call String().

Make sense?

like image 39
Leo Correa Avatar answered Oct 23 '22 09:10

Leo Correa