- 참고사이트 : https://ssipflow.github.io/go/GO-function/
Go lang 특성 정리
- Go 언어는
값에 의한 호출
(Call by value) 만을 지원한다. - 함수 내에서 넘겨받은 변수값을 변경하더라도 함수 밖의 변수에는 영향을 주지 않는다.
- 따라서 함수 밖의 변수의 값을 변경하려면 해당 값이 들어있는 주소값을 넘겨받아, 그 주소에 있는 값을 변경하여 참조에 의한 호출 (Call by reference)을 비슷하게 구현할 수 있다.
값 넘겨주기
- Call by Rerference와 Call by Value의 차이 이해
func CallByValue(num int) {
num++
}
func CallByReference(num *int) {
*num++
}
func main() {
i := 3
CallByValue(i)
fmt.Println(i) // 3
CallByReference(&i)
fmt.Println(i) // 4
}
- CallByValue는 전달받은 i의 값이 함수의 인자 num에 복사된다. 그래서 main 함수의 i가 아닌 CallByValue 함수 안에서만 값이 변경된다.
-
CallByReference는 전달받은 i의 주소가 가리키는 값을 변경한다. 함수 외부의 값을 변경할 수 있다.
- Slice와 Map 자료형은 Call by Value를 쓰는게 아닌,
Call by Referecne
를 사용함
func SliceAddOne(s []int) {
for i := range s {
s[i]++
}
}
func MapAddOne(m map[int]int) {
for k, _ := range m {
m[k]++
}
/*
for _, v := range m {
v++
// 이 경우 m의 주소값이 가리키는 이 변하지 않는다.
}
*/
}
func main() {
n := []int{1, 2, 3, 4}
SliceAddOne(n)
fmt.Println(n) // [2 3 4 5]
m := map[int]int{
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
}
MapAddOne(m)
fmt.Println(m) // map[1:2 2:3 3:4 4:5 5:6]
}
둘 이상의 반환값
- 일반적으로 에러값을 반환할 때 많이 사용하고, 둘 이상의 값을 반환할 때는 반환 타입을 괄호로 둘러싼다.
func WriteTo(w io.Writer, lines []strings) err { ... return err }
func WriteTo(w io.Writer, lines []strings) (int64, err) { ... return n, err }
다중 리턴
func MultiReturnSample() (msg1, msg2 string) {
msg1 = "Hello"
msg2 = "World"
return
}
func main() {
m1, m2 := MultiReturnSample()
fmt.Println(m1, m2) // Hello World
}
_ (언더바)의 사용
- 둘 이상의 반환값이 있을 때, 필요없는 변수의 경우 언더바( _ )를 이용하면 된다.
_, err := WriteTo(w, lines)
- 반복문 요소를 끄집어낼 때, 인덱스만 뽑아내기 =>
_를 뒤에!!
// Example 배열 요소를 순차적 방문(ragne 키워드 이용)
package main
import "fmt"
func main() {
arr := [5]int{12, 34, 23, 56, 34}
for value, _ := range arr {
fmt.Println(value) // 0 1 2 3 4
}
}
- 만약 인덱스를 생략하고 원소 값만을 얻고자 한다면 =>
_를 앞에!
package main
import "fmt"
func main() {
arr := [5]int{12, 34, 23, 56, 34}
for _, value := range arr {
fmt.Println(value) // 0 1 2 3 4
}
}
에러값 주고 받기
- GO에서는 둘 이상의 값을 반환할 수 있기 때문에 여러 반환값 중 마지막 값에 에러를 반환
- GO는 다음과 같은 방식으로 예외를 처리하면서 에러를 반환
func Sample() (int, error) {
if err := MyFunc(); err != nil {
...
return nil, err
}
...
- 새로운 에러를 생성해야 하는 경우
- errors.New 와 fmt.Errorf 를 이용하여 문자열 메시지를 줄 수 있다
return erros.New("stringlist.ReadFrom: line is too long")
// 다른 부가 정보를 추가한 메시지를 돌려줄 수 있다.
// 다음은 몇 번쨰 줄이 너무 긴지, 에러 메시지를 포함한다.
return fmt.Errorf("stringlist: too long line at %d", count)
가변인자 넘겨받기
- 갯수를 모르는 상태에서 자료형이 같은 인자를 함수에 전달
func SliceParams(nums []int) {
fmt.Println("slice parameter")
for _, num := range nums {
fmt.Print(num, " ")
}
fmt.Println()
}
func VariableParams(nums ...int) {
fmt.Println("variable parameter")
for _, num := range nums {
fmt.Print(num, " ")
}
fmt.Println()
}
func main() {
nums := []int{1, 2, 3, 4, 5}
SliceParams([]int{1, 2, 3, 4, 5}) // 1 2 3 4 5
VariableParams(1, 2, 3, 4, 5) // 1 2 3 4 5
// 슬라이스 하나를 넘기면 그 슬라이스 하나를 담고 있는 슬라이스로 만들어 넘겨주기 때문에 점 셋을 붙여 슬라이스를 가변인자로 전달 가능 하다.
VariableParams(nums...) // 1 2 3 4 5
}
함수 리터럴
- 이름이 없는 순수한 함수의 값 (익명함수)
// add 함수
func add(a, b int) int {
return a + b
}
// 함수리터럴
func (a, b int) int {
return a + b
}
- 이러한 리터럴은 변수에 전달하여 변수를 함수처럼 사용가능!
func Example_funcLiteralVal() {
printHello := func() {
fmt.Println("Hello!")
}
printHello() // Hello!
}