쿼카러버의 기술 블로그
[golang] string pointer는 언제쓸까? 본문
Go에서 string은 value다. 따라서, nil이 될 수 없다.
Go has various value types including strings, integers, floats, booleans, etc. |
따라서 아래 코드는 컴파일이 될 수 없다.
x := "I am a string!"
x = nil // Won't compile, strings can't be nil in Go
하지만 string을 가리키는 pointer(*string)는 nil이 될 수 있다. 따라서 아래 코드는 컴파일이 가능하다.
var x *string x = nil
그럼 string pointer는 언제쓸까? 일반적으로 golang에서는 nil이 필요하지 않은 상황이면 string pointer는 사용하는 것을 권장하지 않는다. 일단 value가 있는지 없는지를 아래처럼 확인해야 하는 추가 코드가 필요해지기 때문이다.
func UseString(s *string) error {
if s == nil {
temp := "" // *string cannot be initialized
s = &temp // in one statement
}
value := *s // safe to dereference the *string
}
즉 ""(empty string)와 nil은 다른 개념으로 이해해야 한다.
Use Cases
json, yaml 파일을 struct로 unmarshal(string to golang struct)할 때 아래와 같은 두 가지 케이스를 고려해볼 수 있다.
1) empty string과 null value(입력하지 않은 케이스)를 같은 케이스로 보고 싶을 때
2) empty string과 null value를 구분하고 싶을 때.
이렇게 말하면 이해가 어려우니 예제로 살펴보자.
아래와 같이 jsonDoc과 이를 담을 struct를 정의했다고 해보자. (여기서는 string을 사용)
type Config struct {
Environment string
Version string
HostName string
}
jsonDoc :=`
{
"Environment" : "Dev",
"Version" : ""
}`
위 jsonDoc에는 HostName이 생략돼있는데, 위를 Unmarshal할 경우, Version과 Hostname 모두 ""(empty string)로 저장된다.
이는 string이 value이기 때문에, 비어 있을 경우 default로 ""를 가지기 때문이다.
코드 전문
package main
import (
"encoding/json"
"fmt"
)
type Config struct {
Environment string
Version string
HostName string
}
func (c *Config) String() string {
return fmt.Sprintf("Environment: '%v'\nVersion:'%v'\nHostName: '%v'",
c.Environment, c.Version, c.HostName)
}
func main() {
jsonDoc :=`
{
"Environment" : "Dev",
"Version" : ""
}`
conf := &Config{}
json.Unmarshal([]byte(jsonDoc), conf)
fmt.Println(conf) // Prints
// Environment: 'Dev'
// Version:''
// HostName: ''
}
위 경우에는 HostName이 missing property인데, 이를 허용하고 싶지 않을 때는 어떻게 해야 할까?
아래처럼 처리할때 nil인지 아닌지를 확인해주고, nil일 때는 잘못된 값을 넣어줌으로써 처리할 수 없게 만들 수 있다.
package main
import (
"encoding/json"
"fmt"
)
type ConfigWithPointers struct {
Environment *string // pointer to string
Version *string
HostName *string
}
func (c *ConfigWithPointers) String() string {
var envOut, verOut, hostOut string
envOut = "<nil>"
verOut = "<nil>"
hostOut = "<nil>"
if c.Environment != nil { // Check for nil!
envOut = *c.Environment
}
if c.Version != nil {
verOut = *c.Version
}
if c.HostName != nil {
hostOut = *c.HostName
}
return fmt.Sprintf("Environment: '%v'\nVersion:'%v'\nHostName: '%v'",
envOut, verOut, hostOut)
}
func main() {
jsonDoc :=
`
{
"environment" : "asdf",
"hostName" : ""
}
`
conf := &ConfigWithPointers{}
json.Unmarshal([]byte(jsonDoc), conf)
fmt.Println(conf) // Prints the following:
// Environment: 'asdf'
// Version:'<nil>'
// HostName: ''
}
위 예시를 보면, hostName = ""이, 아무것도 안쓴게 아니라, 실제로 ""로 들어가야 하는 경우에 사용된다. 즉 모든 value들이 다 적혀있는 jsonDoc만 유효하게 하고 싶을 경우 위처럼 처리할 수 있는 것이다.
'[Golang]' 카테고리의 다른 글
[golang] time.After이란? (memory leak 가능성) (0) | 2021.08.30 |
---|---|
[golang] Marshal, Unmarshal 차이 (0) | 2021.08.09 |
[golang] Closure(클로저)란? (0) | 2021.08.02 |
[golang] 콘텍스트 (context)란? - 1탄 간략한 소개 (4) | 2021.08.01 |
[golang] 채널 (channel)이란? - 1탄 간단한 소개 (0) | 2021.08.01 |