함수
1. defer (지연)
해당 함수가 실행을 완료했을 때 실행을 위해 호출 스케줄을 잡는다. defer는 어떤 식으로든 자원을 해제해야 할 때 자주 사용된다. 예를 들어, 파일을 열 때 나중에 해당 파일을 반드시 닫아야 한다. defer를 이용하면 다음과 같이 하면 된다.
f, _ := os.Open(filename)
defer f.Close()
이 방법에는 세 가지 장점이 있다. (1) Close 호출을 Open 호출 가까이에 둬서 이해하기가 쉽고, (2) 함수에 반환문이 여러 개 있더라도(if에 하나가 들어 있고, else에 하나가 들어 있는 것처럼) Close가 두 반환문 앞에서 모두 호출될 것이며, (3) 지연된 함수는 런타임 패닉이 일어나더라도 실행될 것이다.
2. panic & recover
python의 raise 및 Exception에 해당할 것으로 보인다.
import "fmt"
func main() {
defer func() {
str := recover()
fmt.Println(str)
}()
panic("PANIC")
}
포인터
3. pointer & new
func zero(xPtr *int) {
*xPtr = 0
}
func main() {
x := 5
zero(&x)
fmt.Println(x) // x는 0
}
Go에서 포인터는 *(애스터리스크) 문자 다음에 저장된 값의 타입으로 나타낸다. zero 함수에서는 xPtr이 int에 대한 포인터에 해당한다. *는 포인터 변수를 "역참조(dereference)"하는 데도 사용된다. 포인터를 역참조하면 해당 포인터가 가리키는 값에 접근할 수 있다.
*xPtr = 0이라고 쓰면 "int 값 0을 xPtr가 참조하는 메모리 위치에 저장하라"라고 말하는 셈이다. 그렇게 하지 않고 xPtr = 0이라고 쓰면 컴파일로 오류가 발생하는데, xPtr은 int가 아니라 또 다른 *int만 할당할 수 있는 *int이기 때문이다.
마지막으로 변수의 주소를 구할 때는 & 연산자를 사용한다. &x는 *int(int에 대한 포인터)를 반환하는데, x는 int이기 때문이다. 이를 통해 원본 변수의 값을 변경할 수 있다. main 함수에 있는 &x와 zero 함수에 있는 xPtr은 동일한 메모리 위치를 참조한다.
func one(xPtr *int) {
*xPtr = 1
}
func main() {
xPtr := new(int)
one(xPtr)
fmt.Println(*xPtr) // x는 1
}
new는 인자로 타입을 하나 받아 해당 타입의 값에 맞는 충분한 메모리를 할당한 후 그것에 대한 포인터를 반환한다.
Go는 가비지 컬렉션을 지원하는 언어로서 new로 생성한 것을 아무것도 가리키는 것이 없으면 메모리가 자동으로 정리된다.
구조체와 메서드
구조체
type Circle struct {
x, y, r float64
}
4. method
func (c *Circle) area() float64 {
return math.Pi * c.r * c.r
}
func 키워드와 함수명 사이에 "수신자(receiver)"를 추가했다. 수신자는 이름과 타입이 있다는 점에서 매개변수와 비슷하지만 이런 식으로 함수를 생성하면 . 연산자를 이용해 해당 함수를 호출할 수 있다.
5. 포함 타입
type Android struct {
Person
Model string
}
a := new(Android)
a.Talk()
is-a 관계는 이처럼 직관적으로 동작한다. 즉, 사람은 이야기할 수 있고, 안드로이드는 사람이며, 따라서 안드로이드는 이야기할 수 있다.
6. 인터페이스
python의 추상 클래스에 해당하는 것으로 보인다.
구조체(struct)가 필드들의 집합체라면, interface는 메서드들의 집합체이다. interface는 타입(type)이 구현해야 하는 메서드 원형(prototype)들을 정의한다. 하나의 사용자 정의 타입이 interface를 구현하기 위해서는 단순히 그 인터페이스가 갖는 모든 메서드들을 구현하면 된다.
인터페이스
type Shape interface {
area() float64
perimeter() float64
}
인터페이스 구현체
//Rect 정의
type Rect struct {
width, height float64
}
//Circle 정의
type Circle struct {
radius float64
}
//Rect 타입에 대한 Shape 인터페이스 구현
func (r Rect) area() float64 { return r.width * r.height }
func (r Rect) perimeter() float64 {
return 2 * (r.width + r.height)
}
//Circle 타입에 대한 Shape 인터페이스 구현
func (c Circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c Circle) perimeter() float64 {
return 2 * math.Pi * c.radius
}
아래 예제에서 showArea() 함수는 Shape 인터페이스들을 파라미터로 받아들이고 있는데, 따라서 Rect와 Circle 처럼 Shape 인터페이스를 구현한 타입 객체들을 파라미터로 받을 수 있다. showArea() 함수 내에서 해당 인터페이스가 가진 메서드 즉 area() 혹은 perimeter()을 사용할 수 있다.
func main() {
r := Rect{10., 20.}
c := Circle{10}
showArea(r, c)
}
func showArea(shapes ...Shape) {
for _, s := range shapes {
a := s.area() //인터페이스 메서드 호출
println(a)
}
}
7. empty interface (=dynamic type), type assertion
다양한 형을 담기 위한 dynamic type으로 사용된다.
메서드를 전혀 갖지 않는 빈 인터페이스로서, Go의 모든 Type은 적어도 0개의 메서드를 구현하므로, 흔히 Go에서 모든 Type을 나타내기 위해 빈 인터페이스를 사용한다. 즉, 빈 인터페이스는 어떠한 타입도 담을 수 있는 컨테이너라고 볼 수 있으며, 여러 다른 언어에서 흔히 일컫는 Dynamic Type 이라고 볼 수 있다.
Interface type의 x와 타입 T에 대하여 x.(T)로 표현했을 때, 이는 x가 nil이 아니며, x는 T 타입에 속한다는 점을 확인(assert)하는 것으로 이러한 표현을 "Type Assertion"이라 부른다.
참조
한 눈에 끝내는 고랭 기초 - 구름EDU
이미 모두 갖추어진 실습환경에서 직접 코드를 작성하고 실행하며 Go lang의 기본을 다질 수 있는 프로그래밍 강좌입니다.
edu.goorm.io
'Programming > Golang' 카테고리의 다른 글
[번역] Strings, bytes, runes and characters in Go (0) | 2021.07.02 |
---|---|
Go 언어 배우기 (II) - 변수부터 컬렉션까지 (With Python) (0) | 2020.05.12 |
Go 언어 배우기 (I) - 왜, 어떻게? (0) | 2020.05.06 |