không phát hiện trong Go


165

Tôi thấy rất nhiều mã trong Go để phát hiện con số không, như thế này:

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

tuy nhiên, tôi có một cấu trúc như thế này:

type Config struct {
    host string  
    port float64
}

và config là một phiên bản của Config, khi tôi thực hiện:

if config == nil {
}

có lỗi biên dịch, nói rằng: không thể chuyển đổi nil sang kiểu Config


3
Tôi không hiểu tại sao cổng là loại float64?
alamin

2
Không nên như vậy. Api JSON của Go nhập bất kỳ số nào từ JSON vào float64, tôi phải chuyển đổi float64 thành int.
Qian Chen

Câu trả lời:


179

Trình biên dịch đang chỉ ra lỗi cho bạn, bạn đang so sánh một thể hiện cấu trúc và con số không. Chúng không cùng loại nên nó coi đó là sự so sánh không hợp lệ và la mắng bạn.

Những gì bạn muốn làm ở đây là so sánh một con trỏ với thể hiện cấu hình của bạn với nil, đó là một so sánh hợp lệ. Để làm điều đó, bạn có thể sử dụng golang mới dựng sẵn hoặc khởi tạo một con trỏ tới nó:

config := new(Config) // not nil

hoặc là

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

hoặc là

var config *Config // nil

Sau đó, bạn sẽ có thể kiểm tra nếu

if config == nil {
    // then
}

5
Tôi đoán var config &Config // nilnên là:var config *Config
Tomasz Plonka

var config *Configtai nạn với invalid memory address or nil pointer dereference. Có lẽ chúng ta cầnvar config Config
kachar

Tôi hiểu lý do đằng sau sự lựa chọn này có thể không là của bạn, nhưng nó làm cho không có ý nghĩa với tôi rằng "if! (Config! = Nil)" là hợp lệ nhưng điều đó "nếu cấu hình == nil" thì không. Cả hai đang làm một so sánh giữa cùng một cấu trúc và phi cấu trúc.
retorquere

@retorquere cả hai đều không hợp lệ, xem play.golang.org/p/k2EmRcels6 . Cho dù đó là '! =' Hoặc '==' không có sự khác biệt; điều làm nên sự khác biệt là liệu config là struct hay con trỏ tới struct.
stewbasic

Tôi nghĩ điều này là sai, vì nó luôn luôn sai: play.golang.org/p/g-MdbEbnyNx
Madeo

61

Ngoài Oleiade, xem thông số kỹ thuật về các giá trị 0 :

Khi bộ nhớ được phân bổ để lưu trữ một giá trị, thông qua khai báo hoặc lệnh gọi thực hiện hoặc mới và không được cung cấp khởi tạo rõ ràng, bộ nhớ được cung cấp một khởi tạo mặc định. Mỗi phần tử của một giá trị như vậy được đặt thành giá trị 0 cho loại của nó: false cho booleans, 0 cho số nguyên, 0,0 cho số float, "" cho chuỗi và nil cho con trỏ, hàm, giao diện, lát, kênh và bản đồ. Việc khởi tạo này được thực hiện một cách đệ quy, vì vậy, ví dụ, mỗi phần tử của một mảng các cấu trúc sẽ có các trường bằng 0 nếu không có giá trị nào được chỉ định.

Như bạn có thể thấy, nilkhông phải là giá trị 0 cho mọi loại mà chỉ dành cho con trỏ, hàm, giao diện, lát, kênh và bản đồ. Đây là lý do tại sao config == nillà một lỗi và &config == nilkhông.

Để kiểm tra xem cấu trúc của bạn chưa được định hình bạn phải kiểm tra tất cả các thành viên cho giá trị bằng không tương ứng của nó (ví dụ host == "", port == 0vv) hoặc có một lĩnh vực tư nhân được thành lập bởi một phương pháp khởi tạo nội bộ. Thí dụ:

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
Hơn nữa ở trên, đó là lý do tại sao time.Timecó một IsZero()phương pháp. Tuy nhiên, bạn cũng có thể làm var t1 time.Time; if t1 == time.Time{}và bạn cũng có thể làm if config == Config{}để kiểm tra tất cả các trường cho bạn (đẳng thức cấu trúc được xác định rõ trong Go). Tuy nhiên, điều đó không hiệu quả nếu bạn có nhiều lĩnh vực. Và, có lẽ giá trị 0 là giá trị lành mạnh và có thể sử dụng được nên việc chuyển một giá trị trong không đặc biệt.
Dave C

1
Hàm Khởi tạo sẽ thất bại, nếu Cấu hình như một con trỏ được truy cập. Nó có thể được đổi thànhfunc (c *Config) Initialized() bool { return !(c == nil) }
Sundar

@Sundar trong trường hợp này có thể thuận tiện để làm theo cách này, vì vậy tôi đã áp dụng thay đổi. Tuy nhiên, thông thường tôi sẽ không mong đợi kết thúc nhận cuộc gọi phương thức để kiểm tra xem chính nó có phải không, vì đây sẽ là công việc của người gọi.
nemo

16

Tôi đã tạo một số mã mẫu tạo các biến mới bằng nhiều cách khác nhau mà tôi có thể nghĩ ra. Có vẻ như 3 cách đầu tiên tạo giá trị và hai cách cuối cùng tạo tham chiếu.

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)
}

đầu ra nào:

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

6

Bạn cũng có thể kiểm tra như thế nào struct_var == (struct{}). Điều này không cho phép bạn so sánh với nil nhưng nó kiểm tra xem nó có được khởi tạo hay không. Hãy cẩn thận trong khi sử dụng phương pháp này. Nếu cấu trúc của bạn có thể có giá trị bằng không cho tất cả các trường của nó, bạn sẽ không có thời gian tuyệt vời.

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

Thông số ngôn ngữ đề cập đến hành vi của các nhà khai thác so sánh:

Toán tử so sánh

Trong mọi so sánh, toán hạng thứ nhất phải được gán cho loại toán hạng thứ hai hoặc ngược lại.


Phân công

Giá trị x được gán cho một biến loại T ("x được gán cho T") trong bất kỳ trường hợp nào sau đây:

  • Kiểu của x giống hệt với T.
  • x loại V và T có các loại cơ bản giống hệt nhau và ít nhất một trong số V hoặc T không phải là loại được đặt tên.
  • T là một loại giao diện và x thực hiện T.
  • x là giá trị kênh hai chiều, T là loại kênh, loại V và T có các loại phần tử giống hệt nhau và ít nhất một trong số V hoặc T không phải là loại được đặt tên.
  • x là mã định danh được khai báo trước nil và T là một con trỏ, hàm, lát, bản đồ, kênh hoặc loại giao diện.
  • x là hằng số được đánh dấu đại diện bởi một giá trị loại T.

0

Trong Go 1.13 trở lên, bạn có thể sử dụng Value.IsZerophương thức được cung cấp trong reflectgói.

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

Ngoài các loại cơ bản, nó cũng hoạt động cho Array, Chan, Func, Interface, Map, Ptr, Slice, UnsafePulum và Struct. Xem cái này để tham khảo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.