공부/GO

Go interface 구현 시 pointer receiver 오류

제이든초이 2023. 7. 11. 05:46

https://go-tour-ko.appspot.com/methods/25

 

Go를 향한 여행

 

go-tour-ko.appspot.com

Tour of Go 중 interface에 대한 실습(Method와 Interface #25)을 진행하면서 발생했던 오류에 대해서 다룬다. image.Image interface의 구현을 다루는 다음 코드는 오류를 일으킨다.

package main

import "image/color"
import "image"
import "golang.org/x/tour/pic"

type Image struct{
	ImageBounds image.Rectangle
	Color color.Color
}

func (image *Image) ColorModel() color.Model {
	return color.RGBAModel
}

func (image *Image) Bounds() image.Rectangle {
	return image.ImageBounds
}

func (image *Image) At(x, y int) color.Color {
	return image.Color
}
	
func main() {
	m := Image{
		image.Rectangle{image.Point{-100, 25}, image.Point{100, 50}}, 
		color.CMYK{122, 84, 32, 0},
	}
	pic.ShowImage(m)
}

다음은 image.Image에 대한 인터페이스 정의이다.

type Image interface {
	// ColorModel returns the Image's color model.
	ColorModel() color.Model
	// Bounds returns the domain for which At can return non-zero color.
	// The bounds do not necessarily contain the point (0, 0).
	Bounds() Rectangle
	// At returns the color of the pixel at (x, y).
	// At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid.
	// At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one.
	At(x, y int) color.Color
}

겉보기에는 문제가 없어보인다. Image는 직사각형 정보와 단일색을 담고 있는 구조체로 선언되었고, Image는 다음의 인터페이스 정의에 따라 각자의 메서드를 구현하였다. 예를 들어 main() 안의 변수 m 에 대해 m.ColorModel()을 호출하면 color.RGBAModel이 반환된다. 나머지 메서드들도 마찬가지이다. 그러나 결과는 다음과 같다.

go: finding module for package golang.org/x/tour/pic
go: downloading golang.org/x/tour v0.1.0
go: found golang.org/x/tour/pic in golang.org/x/tour v0.1.0
# play
./prog.go:29:16: cannot use m (variable of type Image) as image.Image value in argument to pic.ShowImage: Image does not implement image.Image (method At has pointer receiver)

요약하자면 'Image does not implement image.Image', 즉 Image 구조체가(내가 정의한) 이미지 인터페이스를 올바르게 구현하지 않았으며, At라는 메서드가 pointer receiver를 가지고 있으므로 구현에 실패했다는 것이다. 하지만 분명히 m.At(0, 0)과 같은 호출은 잘 동작할 것이다. 이는 pointer receiver가 값 형식도 받기 때문이다. 그러나 내 생각과는 다르다는 것을 알 수 있다.

이에 대한 대답을 스택오버플로우에서 찾을 수 있었다.

https://stackoverflow.com/questions/33936081/golang-method-with-pointer-receiver

 

Golang method with pointer receiver

I have this example code package main import ( "fmt" ) type IFace interface { SetSomeField(newValue string) GetSomeField() string } type Implementation struct { someField string...

stackoverflow.com

두 번째 대답이 나한테는 더 도움이 됐는데, 요는 이렇다. Type이 가진 메서드를 조사할 때, Type의 포인터 메서드(*T를 받는)는 포함되지 않는다는 것이다. 즉, T라는 타입이 있다고 하면, 타입으로서의 메서드 소유는 T와 *T가 다르다는 것이다. 따라서 pointer method로 정의된 메서드는 *T의 메서드에 해당하는 것이다. 따라서 위의 코드에서도 함수를 호출하는 부분(main)에서 인자 m을 포인터인 &m으로 변경해주어야 한다.

따라서 코드를 다음과 같이 수정한다.

func main() {
	m := Image{
		image.Rectangle{image.Point{-100, 25}, image.Point{100, 50}}, 
		color.CMYK{122, 84, 32, 0},
	}
	pic.ShowImage(&m) // m -> &m
}

결과는 다음과 같다. 코드가 잘 실행되는 것을 볼 수 있다.

요약

1. go의 interface는 메서드 호출 가능 여부가 아니라 타입이 가진 메서드로 판단한다.

2. T와 *T가 가진 메서드는 다르다.

3. 호출할 때 유의해서 호출해주도록 하자.