Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go: interface method call

Tags:

go

I have two questions regarding Go interface from the following code.

type Color interface {
    getColor() string
    setColor(string)
}

type Car struct {
    color string
}
func (c Car) getColor() string {
    return c.color
}
func (c Car) setColor(s string) {
    c.color = s
}

func main() {
    car := Car{"white"}
    col := Color(car)

    car = col.(Car)         // L(1)
    car.setColor("yellow")
    fmt.Println(col)        // L(2)
    fmt.Println(car)
    car.color = "black"
    fmt.Println(col)        // L(3)
    fmt.Println(car)
}

Q1: Is it ok to write L(1) as "car, _ := col.(Car)"?

Q2: L(2) prints "white" not "yellow".

Why is it? L(3) seems print "black" correctly.

Thanks.

like image 835
Tgi Monday Avatar asked Jul 31 '13 04:07

Tgi Monday


People also ask

How do you call an interface in Go?

Go Interface An interface is declared using the type keyword, followed by the name of the interface and the keyword interface .

How do you implement interface in Go lang?

To implement an interface in Go, we just need to implement all the methods in the interface. Here we implement geometry on rect s. The implementation for circle s. If a variable has an interface type, then we can call methods that are in the named interface.

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 you initialize an interface in Go?

Declaring Interface Types Interface describes all the methods of a method set and provides the signatures for each method. To create interface use interface keyword, followed by curly braces containing a list of method names, along with any parameters or return values the methods are expected to have.


2 Answers

Q1:

No, you cannot say car,_ := col.(Car). The reason for this isn't quite obvious. Here's a list of okay statements in L1:

car,ok := col.(Car)
car = col.(Car)
car,_ = col.(Car)
_,ok := col.(Car)

":=" is the short form for declaration/assignment, since car was already declared in that scope, := will give you an error ("no new variable on left side of :="). Putting "ok" there declares a new variable ("ok"), however, the underscore/ignore pseudo-variable does not count as a new variable for purposes of :=.

Edit: To be clear, you can put "ok" or the underscore here because type assertions return both the type-asserted value and a boolean value indicating whether the assertion was successful. If the question was actually about the general case of "_" and not a question on the ":=" operator: no, in the general case you can't do something like

a,_ := 5

Because that statement only returns one value and go won't let you ignore nothing. (You'll get the error: "assignment count mismatch 2 = 1").

Q2:

In Go, methods can be on a pointer or a value/base type. I believe you'll find the following will work fine:

car.setColor("yellow")
//...
func (car Car) setColor(s string) {
    car.color = s
    fmt.Println(car.color)
}

In this code, it will correctly print "yellow". This is because you're passing the method receiver by value. It does, in fact, modify car -- but a different car than you passed it, a car that happens to be a perfect copy of the car that you called the method on. To fix this you need a pointer receiver,

func (car *Car) setColor(s string) {
    car.color = s
}

This will make the changes visible after the call, because you're giving the method the location where car resides, not just the data that it has. To be thorough, there are a handful of cases involving "reference types" (maps, slices, channels) where you can see the side-effects outside a non-pointer method, but these are exceptions to the rule.

Do note that if you give this method a pointer receiver, variables of the type Car no longer implement the interface Color. Instead, the type that implements the interface is *Car (a pointer to a Car). In fact, since pointers are transparent in Go, this is true even if you leave getColor with a non-pointer receiver, but it's usually better form to make all methods on a type implementing an interface on either the pointer or the base type, not a mix of both.

One miscellaneous note since you appear to be learning: there's nothing wrong, per se, with beginning setColor and getColor with lowercase letters. However, be aware that these methods will not be available outside the immediate package you're writing. For them to be visible, they must start with an Uppercase letter.

like image 89
Linear Avatar answered Oct 13 '22 23:10

Linear


In order for setColor to mutate the Car object you expect you have to pass a pointer, your code passed a Car by value, and changes the color of that value, then promptly discarded that copy of the Car value when the method returns

Here is your example changed so that the interface is satisfied by a pointer to Car

func (c *Car) getColor() string {
    return c.color
}
func (c *Car) setColor(s string) {
    c.color = s
}

The link above outputs:

&{yellow}
&{yellow}
&{black}
&{black}
like image 26
BrandonAGr Avatar answered Oct 13 '22 21:10

BrandonAGr