Tgi Monday
Tgi Monday

Reputation: 71

Go: interface method call

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.

Upvotes: 7

Views: 11015

Answers (2)

Linear
Linear

Reputation: 22196

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.

Upvotes: 10

BrandonAGr
BrandonAGr

Reputation: 6017

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}

Upvotes: 2

Related Questions