본문 바로가기

Development/Golang

[Effective Go] functions

Multiple return values

 Golang의 특징 중 하나는 function과 method가 여러 값을 return시킨다는 것이다. os 패키지의 Write method의 형식은 아래와 같다.

func (file *File) Write(b []byte) (n int, err error)

위 method에서 반환되는 값은 작성된 bytes 수(n)와, n != len(b) 일 때 non-nil 상태의 error를 반환한다.

Named result parameters

 return 또는 해당 function 내 결과값은 named 될 수 있다. 또한 일반적인 변수로도 사용된다. named 되었을 때, 해당 타입에 따라 초기화가 수행된다. (int 라면 0) function에서 return이 수행되는 시점에서, 해당 인자의 값이 return 된다.

func nextInt(b []byte, pos int) (value, nextPos int) {
	for ; i < len(b) && !isDigit(b[i]); i++ {
	}
	x := 0
	for ; i < len(b) && isDigit(b[i]); i++ {
		x = x*10 + int(b[i]) - '0'
	}
	return x, i
}

for i := 0; i < len(b); {
	x, i = nextInt(b, i)
	fmt.Println(x)
}
func ReadFull(r Reader, buf []byte) (n int, err error) {
	for len(buf) > 0  && err == nil {
		var nr int
		nr, err = r.Read(buf)
		n += nr
		buf = buf[nr:]
	}
}

Defer

 Golang에서 defer 키워드는 function 내에서 return이 수행되기 직전에 수행된다. 이는 주로 resource의 해제나 mutex의 unlock 등에 주로 사용된다.

// Contents returns the file`s contents as a string
func Contents(filename string) (string, error) {
	f, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer f.Close() // f.Close will run when we`re finished.

	var result []byte
	buf := make([]byte, 100)
	for {
		n, err := f.Read(buf[0:])
		result = append(result, buf[0:n]...) // append is discussed later.
		if err != nil {
			if err == io.EOF {
				break
			}
			return "", err // f will be closed if we return here.
		}
	}
	return string(result), nil // f will be closed if we return here.
}

 defer을 사용하는 것은 아래와 같은 장점을 지닌다.

  1. 위 예시와 같이 file close 관련 구문을 실수로 작성하지 않는 경우를 보완해준다. 또한 return 구문 전에 중복으로 추가할 필요도 없다.
  2. open 구문 근처에 있으면, function의 끝지점에 file close 관련 구문이 있는 것 보다 더욱 명시적이다.

 defer이 선언된 함수는 LIFO 순서를 따르게 된다. 아래의 코드는 4 3 2 1 0 과 같이 출력되고, 반환할 것이다.

for i := 0; i < 5; i++ {
	defer fmt.Printf("%d ", i)
}

 defer가 실행될 때 defer가 선언된 함수의 매개변수가 사용된다는 것을 이용할 수도 있다. 매개변수는 defer가 선언된 시점에 evaluated된다.

func trace(s string) string {
	fmt.Println("entering:", s)
	return s
}

func un(s string) {
	fmt.Println("leaving:", s)
}

func a() {
	defer un(trace("a"))
	fmt.Println("in a")
}

func b() {
	defer un(trace("b"))
	fmt.Println("in b")
	a()
}

func main() {
	b()
}

--------------
entering: b
in b
entering: a
in a
leaving: a
leaving: b

'Development > Golang' 카테고리의 다른 글

[Effective Go] Control Structures  (0) 2021.11.20
[Effective Go] Names  (0) 2021.11.20