Backend with Golang

Effective Go - Part 1

아직개구리 2023. 8. 9. 22:03

Switch 

  1. switch문 밖에 있는 loop을 break하고 싶을때는 label을 사용해서 해당 라벨을 break하면 된다. continue statement 도 마찬가지로 label을 사용할 수 있다. 
  2. Type switch : switch 는 interface variable의 dynamic type을 밝혀내는 것에 쓰이기도 한다. 아래의 예시처럼 되는데, 이때  t:=t.(type)으로 변수 재사용하는 것은 idiomatic하다. 
var t interface{}
t = functionOfSomeType()
switch t:=t.(type) {
	
}

 

defer

  • defer statement는 function call을 function이 return하기 바로 직전에 실행되도록 scheduling한다. 
  • defer를 사용하면 자원이 release되는 것 같은 상황들을 효과적으로 다룰 수 있게 된다. 어떤 경로로 return하는지 상관하지 않아도 된다. block level resource를 다루는데에 익숙한 programmer들에게 defer는 이상해 보일 수도 있지만, 이 기능의 가장 흥미롭고 powerful한 지점은 정확히 말하면 이것이 block based가 아니라 function based라는 사실에서 온다. panic과 recover에서 이것을 더 다룰 것이다. 
  • The arguments to the deferred function은 defer가 executes될때 evaluated된다. call이 실행될 때가 아니라. 하나의 deferred call site가 여러가지 function execution들을 연기할 수 있다는 것을 의미한다. 

Data 

  • new: go 에서 new를 사용하면, type T의 새로운 객체에 제로값이 저장된 공간을 할당하고 그 객체의 주소를 반환한다.  new 를 통해서 반환된 메모리는 제로값을 가지므로 바로 사용할 수 있다. 
  • 때로는 zero값만으로 충분하지 않고 constructor를 사용해서 initialize를 해야하는데, 예를 들면 f:= new(File)한 뒤에 f.fd = fd, f.name =name 이런식으로 초기화를 하는 방식이다. 하지만 이런 방식은 너무 반복되는 코드가 많아지므로, &File{fd: fd, name:name} 이런식으로 초기화 할 수도 있다. 이렇게 되면 안에서 입력되지 않은 field들은 zero값을 가지게 되는 것이다. 
  • make: slices, maps, channels에서만 사용한다. type T의 제로값이 아닌 초기화된 값을 반환한다. 이 세 타입이 사용전 초기화 되어야 하는 데이터 구조를 가리키고 있기 때문이다.  new 와 달리 포인터를 반환하지 않는다. 
  • 배열: var 변수명 [배열 크기] 데이타타입 으로 선언되는데, 이때 배열 크기가 다르면 다른 타입으로 인식된다. Go 에서는 배열은 값으로 여겨지기 떄문에 다른 배열에 할당할 때 모든 요소가 복사된다. 함수에 argument로 배열을 pass할 때, 포인터가 아닌 복사된 배열을 받게 된다. C와 같이 실행되기 바란다면 배열 포인터를 사용하면 되지만, go에서는 slice를 사용한다. 
  • Slice: 배열을 포장함으로써 data의 sequence형태에 대한 편리한 interface를 제공한다. transformation matrix와 같이 명백한 차원을 가지고 있을 때를 빼고는 거의 slice를 사용한다. nil slice에 대해서도 len, cap 함수를 쓸 수 있다. 둘다 0 값을 가진다. slice에 append해서 데이터를 더할 수 있는데 이때 capacity를 초과하면 reallocated된다.  아래와 같이.
newSlice := make([]byte, (l+len(data))*2)
copy(newSlice, slice)
slice = newSlice
  • 2D slices: 메모리를 할당해야하는 때가 오는데, 예를 들면 모든 줄의 pixel값을 읽어야 할 때가 있다. 이럴 떄는 두가지 방법이 있다. 첫번째 방법은 모든 줄의 slice를 독립적으로 할당하는 것과, 하나를 나눠서 배정하는 방법이 있다. 이건 application에 따라 달라지는데, slice가 커지거나 작아질 경우 독립적으로 각각 할당하는 것이 overwriting을 막기에 좋다. 그렇지 않은 경우는 하나로 하는게 효율적이다. 
  • Maps: 편리하고 강력한 내장 데이터 구조. key는 equality연산이 정의되어 있는 어떤 type도가능. equality 가 정의되지 않은 slice는 map의 key로 사용될 수 없음. map은 slice와 마찬가지로 undelying data structure에 대한 참조자를 들고 있는 것이기 때문에 만약 함수안에서 전달받아서 값을 변경하면 그 함수의 caller에게도 그 변화가 보인다. 없는 key를 조회하려고 하면 value 의 type에 해당하는 zero 값을 반환한다. 이 성질을 사용해서  set을 구현할 수도 있다.  아래와 같이 되면, attended[personName] 이렇게 하면 쉽게 없는지 있는지 알 수 있다. zero value와 구별해서 진짜 있는지 없는지 알아야할 때가 있으면 comma ok idiom을 사용하면 된다. delete를 써서 엔트리를 삭제할 수 있고, 이 built in function delete는 key가 없어도 쓸 수 있다.
attended := map[string]bool{
.. }
  • Printing : default conversion 을 원한다면, 예를 들어 integer은 decimal인 것처럼. %v 를 사용하면 된다. struct를 프린팅하려면, %+v 를 사용하면, field name까지 붙어서 나오고, %#v를 사용하면, 어떤 값이든 완전한 Go syntax문법을 가지게 출력한다.