Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slice vs type alias as the method receiver

Tags:

methods

types

go

I have a struct Foo with a method print:

type Foo struct {
    Bar string
}
func (f Foo) print() {
    fmt.Println(f.Bar)
}

If I want to print for a slice of Foo, the canonical way perhaps is to write a for loop, and have a function to encapsulate it:

func printFoos(fs []Foo) {
    for _, f := range fs {
        f.print()
    }
}
printFoos([]Foo{})

Coming from an OOP background, I find this approach a bit unappealing.

What I would like to do is to associate printFoos with []Foo:

// Invalid Go code
func (fs []Foo) print() {
    for _, f := range fs {
        f.print()
    }
}

The above doesn't work because in Go, an unnamed type can't be used as the method receiver, as discussed in this Google Group thread.

To circumvent it, it is possible to write:

type Foos []Foo
func (fs Foos) print() {
    for _, f := range fs {
        f.print()
    }
}

To use it, I have to explicitly declare the type as Foos, so still I can't use print on []Foo

fs := []Foo{}
fs.print() // error
var fss Foos = fs
fss.print()

What I'm confused about is, in the above code, fss and fs are clearly of the same type, as I can assign fs to fss without error. However, we can't simply use fs.print() and let Go be smart about the conversion.

Why is this the case?

The full code can be found on playground.

like image 227
z11i Avatar asked Jul 24 '26 07:07

z11i


1 Answers

What I'm confused about is, in the above code, fss and fs are clearly of the same type, as I can assign fs to fss without error.

You jump to the wrong conclusion. Having the same type is not a must have requirement for assignability.

fss has type Foos, and fs has type []Foo, an unnamed slice type. It's true that they have the same underlying type, that's why you can assign fs to fss, covered in this assignability rule:

A value x is assignable to a variable of type T ("x is assignable to T") if one of the following conditions applies:

  • ...
  • x's type V and T have identical underlying types and at least one of V or T is not a defined type.

Methods are bound to concrete types. So the Foos.print() method is not available for a value of a different type, including []Foo.

But you don't need to create a variable just to call that method, you may simply use a type conversion:

Foos(fs).print()

This conversion does not change the memory layout, just the type, so it's safe and efficient. We used it only to gain access to a method of a type with identical underlying type.

like image 169
icza Avatar answered Jul 27 '26 05:07

icza