Go의 무 탐지


165

Go에서 다음과 같이 nil을 감지하는 많은 코드가 있습니다.

if err != nil { 
    // handle the error    
}

그러나 다음과 같은 구조체가 있습니다.

type Config struct {
    host string  
    port float64
}

그리고 config는 Config의 인스턴스입니다.

if config == nil {
}

컴파일 오류가 있습니다 : nil을 Config 유형으로 변환 할 수 없습니다.


3
왜 포트가 float64 유형인지 이해할 수 없습니다.
alamin

2
해서는 안됩니다. Go의 JSON API는 JSON에서 float64로 숫자를 가져옵니다 .float64를 int로 변환해야합니다.
Qian Chen

답변:


179

컴파일러가 오류를 가리키고 있습니다. 구조 인스턴스와 nil을 비교하고 있습니다. 그것들은 같은 타입이 아니기 때문에 그것을 잘못된 비교로 간주하고 당신에게 소리칩니다.

여기서하려는 것은 구성 인스턴스에 대한 포인터를 nil과 비교하는 것입니다. 이는 유효한 비교입니다. 그렇게하려면 golang 내장을 사용하거나 포인터를 초기화하십시오.

config := new(Config) // not nil

또는

config := &Config{
                  host: "myhost.com", 
                  port: 22,
                 } // not nil

또는

var config *Config // nil

그럼 당신은 확인할 수 있습니다

if config == nil {
    // then
}

5
I의 추측 var config &Config // nil해야한다 :var config *Config
토마스 Plonka

var config *Config와 충돌합니다 invalid memory address or nil pointer dereference. 어쩌면 우리는var config Config
kachar

나는이 선택 뒤에 추론은 당신하지 않을 수 있습니다 이해하지만, 그것은하지 않습니다 "경우! (구성! = 전무)"유효 함을 나에게 의미가 있지만 "설정 == 전무는 경우"아니라고. 둘 다 동일한 구조체와 비 구조체를 비교하고 있습니다.
retorquere

@retorquere 둘 다 유효하지 않습니다 . play.golang.org/p/k2EmRcels6을 참조하십시오 . '! ='이든 '=='이든 차이는 없습니다. 차이점은 구성이 구조 체인지 구조체 포인터인지입니다.
stewbasic

나는 이것이 항상 거짓이기 때문에 이것이 잘못이라고 생각합니다. play.golang.org/p/g-MdbEbnyNx
Madeo

61

Oleiade 외에도 제로 값에 대한 사양을 참조하십시오 .

선언 또는 make 또는 new 호출을 통해 값을 저장하기 위해 메모리가 할당되고 명시적인 초기화가 제공되지 않으면 메모리에 기본 초기화가 제공됩니다. 이러한 값의 각 요소는 해당 유형의 0 값으로 설정됩니다. 부울의 경우 false, 정수의 경우 0, 부동 소수점의 경우 0.0, 문자열의 경우 "", 포인터, 함수, 인터페이스, 슬라이스, 채널 및 맵의 경우 nil. 이 초기화는 재귀 적으로 수행되므로 예를 들어 값을 지정하지 않으면 구조체 배열의 각 요소에는 필드가 0이됩니다.

보시다시피, nil모든 유형의 0 값이 아니라 포인터, 함수, 인터페이스, 슬라이스, 채널 및 맵의 경우에만 0입니다. 이것이 왜 config == nil오류이고 &config == nil그렇지 않은 이유 입니다 .

당신의 구조체가 (예를 들어, 당신은 각각의 제로 값에 대해 모든 구성원을 확인해야 할 것 초기화되지 않은되어 있는지 여부를 확인하려면 host == "", port == 0등) 또는 내부 초기화 방법에 의해 설정되는 개인 필드가 있습니다. 예:

type Config struct {
    Host string  
    Port float64
    setup bool
}

func NewConfig(host string, port float64) *Config {
    return &Config{host, port, true}
}

func (c *Config) Initialized() bool { return c != nil && c.setup }

4
time.TimeIsZero()방법 외에도 방법이 있습니다. 그러나 당신은 또한 할 수 var t1 time.Time; if t1 == time.Time{}있고 당신 을 위해 모든 필드를 점검 수도 if config == Config{}있습니다 (구조 평등은 Go에 잘 정의되어 있습니다). 그러나 필드가 많으면 효율적이지 않습니다. 그리고 아마도 0 값은 제정신이며 사용 가능한 값이므로 1을 전달하는 것은 특별하지 않습니다.
Dave C

1
포인터로 구성에 액세스하면 초기화 된 기능이 실패합니다. 다음으로 변경 될 수 있습니다func (c *Config) Initialized() bool { return !(c == nil) }
Sundar

이 경우 @Sundar는 이렇게하는 것이 편리 할 수 ​​있으므로 변경 사항을 적용했습니다. 그러나 일반적으로 메소드 호출의 수신 끝이 자체가 nil인지 확인하지 않을 것입니다. 이는 호출자의 작업이기 때문입니다.
nemo

16

내가 생각할 수있는 다양한 방법을 사용하여 새 변수를 만드는 샘플 코드를 만들었습니다. 처음 세 가지 방법으로 값을 만들고 마지막 두 가지 방법으로 참조를 만듭니다.

package main

import "fmt"

type Config struct {
    host string
    port float64
}

func main() {
    //value
    var c1 Config
    c2 := Config{}
    c3 := *new(Config)

    //reference
    c4 := &Config{}
    c5 := new(Config)

    fmt.Println(&c1 == nil)
    fmt.Println(&c2 == nil)
    fmt.Println(&c3 == nil)
    fmt.Println(c4 == nil)
    fmt.Println(c5 == nil)

    fmt.Println(c1, c2, c3, c4, c5)
}

어떤 출력 :

false
false
false
false
false
{ 0} { 0} { 0} &{ 0} &{ 0}

6

당신은 또한 같은 확인할 수 있습니다 struct_var == (struct{}). 이를 통해 nil과 비교할 수는 없지만 초기화되었는지 여부를 확인합니다. 이 방법을 사용하는 동안주의하십시오. 구조체가 모든 필드에 대해 0 값 을 가질 수 있다면 좋은 시간이 없습니다.

package main

import "fmt"

type A struct {
    Name string
}

func main() {
    a := A{"Hello"}
    var b A

    if a == (A{}) {
        fmt.Println("A is empty") // Does not print
    } 

    if b == (A{}) {
        fmt.Println("B is empty") // Prints
    } 
}

http://play.golang.org/p/RXcE06chxE


3

언어 스펙 비교 연산자 '행동을 언급한다 :

비교 연산자

비교할 때, 첫 번째 피연산자는 두 번째 피연산자의 유형에 지정 가능하거나 그 반대로 지정 가능해야합니다.


할당 가능성

다음과 같은 경우 x 값은 T 유형의 변수에 할당 가능합니다 ( "x는 T에 할당 가능").

  • x의 유형은 T와 동일합니다.
  • x의 유형 V와 T는 동일한 기본 유형을 가지며 V 또는 T 중 적어도 하나는 명명 된 유형이 아닙니다.
  • T는 인터페이스 유형이며 x는 T를 구현합니다.
  • x는 양방향 채널 값이고, T는 채널 유형이며, x의 유형 V와 T는 동일한 요소 유형을 가지며 V 또는 T 중 적어도 하나는 명명 된 유형이 아닙니다.
  • x는 미리 선언 된 식별자 nil이고 T는 포인터, 함수, 슬라이스, 맵, 채널 또는 인터페이스 유형입니다.
  • x는 유형 T의 값으로 표현할 수있는 형식화되지 않은 상수입니다.

0

Go 1.13 이상에서는 패키지로 Value.IsZero제공되는 방법 을 사용할 수 있습니다 reflect.

if reflect.ValueOf(v).IsZero() {
    // v is zero, do something
}

기본 유형 외에도 Array, Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer 및 Struct에서도 작동합니다. 참고 이를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.