github에서 publishing할 때 branch의 이름이 gh-pages여야 하는 것 처럼
Go
에서도 compile할 메인파일은 꼭 main.go로 해야한다
그리고 string은 double quotes("")로 감싸줘야한다
package main
import "fmt"
func main() {
fmt.Println("Hello world!")
}
위에 Println()
함수를 보면 알겠지만 첫 글자가 대문자이다
Go
는 import
하기 굉장히 편리하게 export
하고 싶은 함수는 첫글자를 대문자로 쓰게하고 있다
something.go라는 파일을 하나 만들고
package something
import "fmt"
func SayHello() {
fmt.Println("Hello")
}
라고 작성하고 main.go에서 이를 import
할 때는
package main
func main() {
something.SayHello()
}
하면 된다
결국 fmt
도 내장된 package
이고 그 안에 수 많은 함수중에 Println()
을 불러서 쓴 것일 뿐이다
const num int = 50
var name string = "alpaca"
Go
는 C
와 매우 비슷하기 때문에 type을 지정해줘야 한다
근데 너무 길고 귀찮으니 함수 안에서 처리할 때에는
package main
func main() {
name := "alpaca"
age := 20
}
과 같이 :=
를 사용하면 알아서 type을 지정해준다
📌 대신 이렇게하면 type은 임의로 변경할 수 없다는 점에 유의하도록 하자
Go
는 function에서 return하는 것들도 모두 type을 정해줘야한다
(TS, C, Java 등을 쓴 경험이 있다면 이해하는게 어렵지 않을 것 같다)
이러한 Statically typed(정적 타입)의 특징은 compile시 속도가 빠르다는 것이다
자세한 건 Why we switched from Python to Go을 참고하도록 하자
그래서 함수를 만들때도
package main
import "fmt"
func lenAndUpper(name string) (int, string) {
return len(name), strings.ToUpper(name)
}
func main() {
totalLength, upperName := lenAndUpper("alpaca")
fmt.Println(totalLength, upperName)
}
// 6 ALPACA
와 같이 type을 지정해 주어야 한다
lenAndUpper()
에서 한가지만 return받고 싶을 때에는
totalLength, _ := lenAndUpper("alpaca")
와 같이 underscore( _ )를 사용해주면 된다
그리고 2개이상의 arguments들을 받고 싶을 때에는
package main
import "fmt"
func repeatName(names ...string) {
fmt.Println(names)
}
func main() {
repeatName("alpaca", "cat", "dog", "pepe")
}
// [alpaca cat dog pepe]
...type을 사용하여 받을 수 있다
개인적으로 Go
의 함수에서 가장 마음에 드는 것은 naked return이다
package main
import "fmt"
func lenAndUpper(name string) (length int, uppercase string) {
length = len(name)
uppercase = strings.ToUpper(name)
return
}
func main() {
totalLength, upperCase := lenAndUpper("alpaca")
fmt.Println(lenAndUpper(totalLength, upperCase)
}
// 6 ALPACA
위와 같이 return할 변수들을 굳이 꼭 명시하지 않아도 된다
왜냐하면 (length int, uppercase string)
이 부분에서 이미 명시했기 때문이다
또 하나의 마음에 드는 것은 defer다
package main
import "fmt"
func lenAndUpper(name string) (length int, uppercase string) {
defer fmt.Println("I'm done.")
length = len(name)
uppercase = strings.ToUpper(name)
return
}
func main() {
totalLength, upperCase := lenAndUpper("alpaca")
fmt.Println(lenAndUpper(totalLength, upperCase)
}
// I'm done.
// 6 ALPACA
defer는 말 그대로 미룬다는 말인데 함수의 실행이 다 끝나고 나서 실행이 된다
main()
함수 안에 있는totalLength, upperCase := lenAndUpper("alpaca")
에서
lenAndUpper()
함수의 실행이 끝나서 먼저I'm done.
이라는 문장이 출력되는 것이다
Go
에서는 오직 for에서 loop를 사용한다
js
처럼 forEach, map()같은게 없다는 말이다
package main
import "fmt"
func superAdd(numbers ...int) int {
for index, number := range numbers {
fmt.Println(index, number)
}
return 0
}
func main () {
superAdd(1, 2, 3, 4, 5, 6)
}
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5
// 5 6
range는 자동적으로 index를 만들어준다
func superAdd(numbers ...int) int {
for i := 0; i < len(numbers); i++ {
fmt.Println(numbers[i])
}
return 0
}
func main () {
superAdd(1, 2, 3, 4, 5, 6)
}
고전적인 방법으로는 위와 같이도 짤 수 있겠지만 나는 조금이라도 짧은 코드를 선호한다
package main
import "fmt"
func canIDrink(age int) bool {
if age < 18 {
return false
} else {
return true
}
}
func main () {
fmt.Println(canIDrink(16))
}
// false
위와 같이 기본 if문을 쓸 수 있다
근데 재밌는 건 if문을 쓰는 순간 variable expression
이 가능하다는 거다
if문의 조건문에서 변수를 만들 수 있다는 거다
package main
import "fmt"
func canIDrink(age int) bool {
if koreanAge := age + 2; koreanAge < 18 {
return false
} else {
return true
}
}
func main () {
fmt.Println(canIDrink(16))
}
// true
package main
import "fmt"
func canIDrink(age int) bool {
switch {
case age < 18:
return false
case age >= 18:
return true
}
return false
}
func main () {
fmt.Println(canIDrink(16))
}
// false
위와 같이 if-else를 남용하지 않을 때 사용 할 수 있다
switch도 마찬가지로 variable expression
을 할 수 있으므로 이를 활용하면
좀 더 가독성 좋은 코드를 작성할 수 있다
pointer는 C
를 공부할 때도 진짜 이해하기 힘들었던 부분이다
Low level programming
을 내가 혐오하는 이유다
package main
import "fmt"
func main() {
a := 2
b := a
fmt.Println(a, b)
}
// 2 2
위 상황에서 b에 a의 값을 assignment했기 때문에 같은 것이라고 생각 할 수 있는데
package main
import "fmt"
func main() {
a := 2
b := a
fmt.Println(&a, &b)
}
// 0xc0000140c0 0xc0000140c8
각각의 메모리의 주소를 보면 다른 것을 알 수 있다
그렇다면 b := &a
로 메모리의 주소값을 저장한 후 이를 출력한다면 둘이 같은 값을 같는 것이 된다
package main
import "fmt"
func main() {
a := 2
b := a
fmt.Println(a, *b)
// 2 2
}
위와 같이 각 변수가 완전히 같은 값을 바라보고 있게 된다
a := 2
b := a
a = 10
fmt.Println(a, b)
// 10 2
만약 그냥 값을 assignment해줬다면 위와 같이 a = 10
으로 변경해도 b에는 영향이 없다
그냥 폴더를 복사 붙여넣기를 한 것 같은 느낌이다
a := 2 b := &a a = 10
fmt.Println(a, b)
// 10 10
위와 같이 `&`으로 주소 값을 저장해주고 `*`으로 그 값을 불러온다면 `a = 10`이 b에게도 영향을 주게 된다
> 폴더를 복사 붙여넣기를 한 것이 아니고 a라는 폴더와 a라는 폴더를 갈 수 있는 b라는
> 바로가기 파일이 있다고 생각하면 된다
![](https://velog.velcdn.com/images%2Fzxcvbnm5288%2Fpost%2Fe6eba101-c3ff-485e-9c99-ab2689ed5d88%2Fimage.png)
이는 굉장히 큰 데이터를 다룰 때 유용할텐데 엄청 용량이 큰 폴더를 복사할 때는 엄청나게 큰 시간이 걸리고
이를 수정하면 다시 assignment해줘야 하는데 경로를 넣어준 것이므로 따로 변경할 일이 없다
그리고 바로가기를 통해 들어간 폴더의 내용을 수정할 수 있듯이 `*b = 20`으로 a의 값을 변경 할 수도 있다
## array and slices
`Go`는 배열의 길이를 명시해주고 타입도 명시해줘야한다
```go
names := [3]string{"alpaca", "pepe"}
근데 배열의 길이를 정하기 싫을 때(혹은 정할 수 없을 때)는 어떻게 해야할까?
이를 위해 있는게 slice다
names := []string{"alpaca", "pepe"}
굉장히 간단하면서 의미도 잘 전달한다
하지만 배열이였다면 남은공간에 assignment해주면 될텐데 (ex. names[2] = "cat"
)
slice에서는 에러가 발생한다
slice에 추가를 할 때에는 append()
라는 함수를 사용해야 한다(ex. append(names, "cat")
)
근데 이 함수는 수정된 slice를 return해 준다
즉 names
는 바뀌지 않았다는 거다
그래서 사용을 할 때에는 names = append(names, "cat")
이런식으로 다시 assignment까지 해줘야 한다
map은 데이터 구조로써 js의 object와 약간 비슷하다
alpaca := map[string]string{"name": "alpaca", "age": "30"}
map은 key: value 구조로 되어있고 정해진 type만 저장할 수 있다
for _, value := range alpaca {
fmt.Println(value)
}
위와 같이 map에서도 for loop를 사용할 수 있다
struct는 map보다 좀 더 유연하게 사용할 수 있다
map에서의 value는 정해진 type으로만 저장할 수 있었다면 struct에서는 아니다
type person struct {
name string
age int
}
alpaca := person{"alpaca", 30}
하지만 위의 경우는 struct의 구조도 살펴봐야 하기 때문에
alpaca := person {name: "alpaca", age: 30}
이런 식으로 key와 value를 한 눈에 볼 수 있게 짜는 것이 더 좋다고 생각한다
reference