값을 둘 이상 반환하기
결과 타입을 괄호 안에 나열합니다. 값은 쉼표로 구분해 반환하고, 받을 때도 같은 방식으로 받습니다.
내장 함수 min과 max는 Go 1.21부터 있습니다. 호출하는 쪽은 모든 결과를 받아야 합니다. x := minMax(nums)는 assignment mismatch: 1 variable but minMax returns 2 values로 컴파일에 실패합니다.
(value, error) 관례
값을 두 개 반환하는 가장 흔한 이유는 실패를 알리기 위해서입니다. 관례상 오류는 마지막에 오고, 오류가 nil이 아니면 나머지 결과는 의미 없는 값으로 취급해야 합니다.
출력:
port: 8080
error: port "http": strconv.Atoi: parsing "http": invalid syntax
error: port out of range
실패하면 나머지 결과로 모두 제로 값(0, "", nil)을 반환하세요. 감싸기와 확인을 포함한 전체 패턴은 오류 처리 페이지에 있습니다.
comma-ok 관용구
일부 내장 연산은 첫 번째 값이 진짜인지 알려 주는 두 번째 bool을 선택적으로 반환합니다. 이 변수는 관례상 ok라고 부릅니다.
이 세 가지 형태(맵 조회, 타입 단언, 채널 수신)는 특별합니다. 대입하는 변수 개수에 따라 값을 하나 또는 둘 반환합니다. 직접 만든 함수는 그렇게 할 수 없습니다. 결과가 둘로 선언된 함수는 항상 둘을 반환합니다.
빈 식별자
_는 값을 받아서 버립니다. 필요 없는 결과에 쓰세요:
_, err := fmt.Sscan(input, &n)
key, _ := parseLine(line)
_로 오류를 무시해도 컴파일은 되므로, 호출이 실패할 수 없거나 실패해도 상관없다는 것을 알 때만 그렇게 하세요. 예를 들어 strings.Builder.WriteString은 항상 nil 오류를 반환하지만, 쓰기를 마친 파일의 close는 그렇지 않습니다.
이름 있는 결과
결과 매개변수에 이름을 붙일 수 있습니다. 함수 시작 부분에서 선언되어 제로 값으로 초기화되고, 값 없는 return은 그 변수들이 담고 있는 값을 반환합니다.
이름은 go doc 출력에 나타나므로 두 결과의 타입이 같을 때 도움이 됩니다. (quotient, remainder int)가 (int, int)보다 많은 것을 말해 줍니다.
긴 함수의 값 없는 return은 읽기 어렵습니다. 결과에 무엇이 담겼는지 보려면 위로 거슬러 올라가 훑어봐야 하기 때문입니다. 보통의 조언은 이렇습니다. 이름이 무언가를 설명해 줄 때 결과에 이름을 붙이고, 함수가 몇 줄짜리가 아니라면 return quotient, remainder처럼 명시적으로 쓰세요.
이름 있는 결과와 defer
지연된 함수는 return 문이 결과를 설정한 뒤, 그리고 호출자가 결과를 받기 전에 실행됩니다. 결과에 이름이 있으면 지연된 함수가 그 값을 읽고 바꿀 수 있습니다. 함수가 반환하는 모든 오류에 맥락을 덧붙이거나 패닉을 오류로 바꿀 때 이 방법을 씁니다.
이 코드는 load : empty name을 출력한 다음 <nil>을 출력합니다. return errors.New(...)는 먼저 err에 대입하고, 그다음 지연된 함수가 그것을 감쌉니다. 자세한 내용은 defer에 있습니다.
이름 있는 결과 섀도잉
이름 있는 결과는 함수의 가장 바깥 스코프에 있습니다. 중첩된 블록 안의 :=는 같은 이름의 새 변수를 만들고, 그러면 값 없는 return은 바깥 변수를 반환합니다.
func find() (n int, err error) {
if true {
n, err := compute() // new n and err, shadowing the results
_ = n
_ = err
}
return // returns 0, nil
}
최악의 경우는 컴파일러가 잡아 줍니다. 결과가 섀도잉된 블록 안의 값 없는 return은 result parameter err not in scope at return으로 실패합니다. 하지만 위 코드처럼 return이 블록 밖에 있는 경우는 잡지 못합니다. 결과 변수를 의도했다면 := 대신 =를 쓰세요.
흔한 실수
- err가 nil이 아닌데 값을 사용함.
err부터 확인하세요. 함수는 오류와 함께 쓰레기 값을 반환해도 됩니다. - 타입이 있는 nil 포인터를 error로 반환함.
var e *MyErr; return e는 nil이 아닌error를 반환합니다. 성공했을 때는 리터럴nil을 반환하세요. - 결과가 너무 많음. 의미가 제각각인 결과가 셋 이상이면 쓰기 어렵습니다. 대신 구조체를 반환하세요.
자주 묻는 질문
Go 함수에서 여러 값을 반환하려면 어떻게 하나요?
결과 타입을 괄호 안에 나열하고, 값을 쉼표로 구분해 반환합니다: func minMax(xs []int) (int, int) { ...; return lo, hi }. 호출하는 쪽은 한 번에 모두 받습니다: lo, hi := minMax(nums).
Go에서 반환 값 중 하나를 무시하려면 어떻게 하나요?
빈 식별자 _에 대입합니다: _, err := strconv.Atoi(s) 또는 n, _ := strconv.Atoi(s). 값을 아예 빼 버릴 수는 없습니다. 왼쪽 변수 개수가 결과 개수와 같아야 합니다.
Go에서 이름 있는 반환 값이란 무엇인가요?
func split(n int) (half, rest int)처럼 이름이 붙은 결과 매개변수입니다. 제로 값으로 시작하고, 본문에서 대입할 수 있으며, 값 없는 return은 현재 값을 반환합니다. 지연된 함수가 호출자가 받을 값을 바꿀 수 있게도 해 줍니다.
Go에서 naked return을 써도 되나요?
짧은 함수에서만 쓰세요. 긴 함수의 값 없는 return은 무엇이 반환되는지 숨기고 섀도잉 버그를 쉽게 만듭니다. Go 스타일 가이드는 문서화를 위해 결과에 이름을 붙이되, 몇 줄이 넘는 함수에서는 값을 명시해 반환하라고 권합니다.