이동 방법: 값 수신기 유형에 대한 포인터
Avoid Using Value Receiver Type in Golang Structs Methods
이 기사에 대해
이 기사에서는 독자가 Go 프로그래밍 언어에서
struct
, methods
및 goroutine
에 대한 기본 지식을 이미 알고 있다고 가정합니다.이 문서의 주요 목적은 구조체 메서드(Go 프로그래밍 언어)에서 값/포인터 수신기 유형의 사용에 대해 스스로 상기시키는 것입니다.
나는 이것에 대해 쓰기로 결정했습니다. 이것은 매우 간단한 일이지만,
구조체 메서드에서 값 수신기 유형을 사용하여 발생하는 문제를 디버깅하는 데 1시간 이상을 보낸 후.
아래 예에서는 값 수신자를 사용하여 발생한 문제를 경고 없이 보여줍니다.
구조체 메서드의 수신자 유형
Golang에서는 객체 지향 프로그래밍 언어의 메서드와 유사한 구조체에 대한 메서드를 정의합니다.
OO 언어와의 차이점은 Golang에는
value
및 pointer
수신기 유형의 두 가지 유형의 수신기가 있다는 것입니다.포인터 리시버 유형은 '객체/구조체'의 포인터를 메모리에 복사합니다. 이는 메서드에서 변경된 사항이 메모리의 실제 '객체/구조체'를 변경하는 OO 프로그래밍 언어의 메서드와 동일합니다.
포인터 수신기 유형의 예는 다음과 같습니다.
// Define a structtype Book struct {
Title string
}
// A pointer receiver type method.
// Marked with the asterisk (*) sign.
func(b *Book) ChangeTitle(title string) {
b.Title = title
}
값 수신자 유형은 '객체/구조체' 데이터를 복사합니다. 메서드에서 객체/구조체에 대한 변경 사항은 메모리의 실제 '객체/구조체'를 변경하지 않습니다.
값 수신자 유형의 예는 다음과 같습니다.
// Value receiver type.
// Marked with no asterisk (*) sign, in the struct receiver.
func (b Book) ChangeTitle(title string) {
b.Title = title
}
Golang에서는 메서드가 구조체 데이터를 변경할 필요가 없는 경우에만 값 수신자를 사용합니다.
그러나 모든 경우에 해당되는 것은 아닙니다. 이 기사에서는 '읽기 전용' 방식으로 포인터 수신기를 사용해야 하는 예를 보여 드리겠습니다.
값 포인터 유형
비효율 할당의 예 경고
package main
import (
"fmt"
)
type book struct {
title string
}
// Change the title of a book
func (b book) changeTitle(title string) {
b.title = title
}
type library struct {
books []book
}
func (lib library) addABook(book book) {
lib.books = append(lib.books, book)
}
func main() {
lib := library{}
fmt.Println(lib)
b := book{title: "Three little pigs"}
b.changeTitle("Blue Birds")
lib.addABook(b)
fmt.Println(lib)
}
Go 언어 서버(vscode 확장),
gopls
는 ineffective-assignment
및 changeTitle
메서드의 할당에 대해 addABook
경고를 표시합니다.이는 포인터 수신기 유형을 사용하는 것을 잊은 경우 버그를 방지하는 데 매우 유용합니다.
고루틴 구현의 예
포인터 수신기가 필요하지만 값 수신기가 사용되지만 경고가 표시되지 않는 다른 조건도 있습니다.
이 상황은 아래 코드와 같이
goroutine
를 사용하면 발생합니다.import (
"fmt"
"time"
)
type book struct {
title string
}
type library struct {
books []book
}
func (lib *library) addABook(book book) {
fmt.Printf("Add book '%s'\n", book.title)
lib.books = append(lib.books, book)
}
// Method below is mimicking time consuming operation.
// Method below is a Value receiver type.
func (lib library) timeConsumingOperation() {
// `Sleep` to mimick ` time-consuming operation.
time.Sleep(5 * time.Millisecond)
// Check the number of books
fmt.Printf("Value receiver type. Found %d books.\n", len(lib.books))
}
func main() {
lib := library{}
// Goroutine mimicking a time consuming operation.
go lib.timeConsumingOperation()
// Add 2 books
go lib.addABook(book{title: "Rain Rain Go Away"})
go lib.addABook(book{title: "Rainbow After Rain"})
// `Sleep` to make sure all goroutines are completed
time.Sleep(2 * time.Second)
fmt.Printf("Actual number of books : %d books.\n", len(lib.books))
}
위의 코드를 실행하면 잘못된 결과가 생성됩니다.
Library
의 timeConsumingOperation
에는 오래된 버전이 있습니다.(방법이
goroutine
로 실행되었기 때문에).Add book 'Rainbow After Rain'
Add book 'Rain Rain Go Away'
Value receiver type. Found 0 books.
Actual number of books : 2 books.
포인터 수신기 유형
데이터 무결성을 원하는 경우 구조체 메서드에서 포인터 수신기 유형을 사용하는 것이 필수입니다.
물론 값 수신자 유형을 사용해야 하는 경우는 적습니다.
timeConsumingOperation
에 포인터 수신기 유형이 있는 이전 예제의 수정은 아래에서 볼 수 있습니다(별표(*) 추가).func (lib *library) timeConsumingOperation() {
timeConsumingOperation
에서 포인터 유형을 사용하면lib
가 메모리의 동일한 데이터를 가리키도록 하여 데이터 무결성을 보장합니다.수정된 코드를 실행하면 다음이 생성됩니다.
Add book 'Rainbow After Rain'
Add book 'Rain Rain Go Away'
Value receiver type. Found 2 books.
Actual number of books : 2 books.
결론
값 수신기 유형 대신 메서드에서 포인터 수신기를 사용하면 데이터 무결성이 보장됩니다.
몇 가지 경우를 제외하고 항상 포인터 수신기 유형을 사용하여 잠재적인 버그를 방지해야 한다고 제안합니다.
위의 경우와 같은 도구로는 잡을 수 없습니다.
다음은 Code xample repository in Github.com의 링크입니다.
이 기사가 유용할 수 있기를 바라며 읽어 주셔서 감사합니다.
Reference
이 문제에 관하여(이동 방법: 값 수신기 유형에 대한 포인터), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/marsonparulian/go-method-pointer-over-value-receiver-typ-ko9텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)