Структуры не могут быть равны нулю, но это компиляция

#go

# #Вперед

Вопрос:

Я играю с Go Playground и нахожу этот код:

 package main

import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e *MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return amp;MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    if err := run(); err != nil {
        fmt.Println(err)
    }
}
 

Итак, здесь я вижу, что *MyError реализует error интерфейс. Однако, если я удаляю amp; in error func и возвращаю MyError вместо этого, я получаю ошибку времени компиляции:
prog.go:19: cannot use MyError literal (type MyError) as type error in return argument: MyError does not implement error (Error method has pointer receiver) . ХОРОШО, я могу это понять, поэтому я могу создать Error такую функцию, и она будет успешно скомпилирована и запущена:

 func (e MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return MyError{
        time.Now(),
        "it didn't work",
    }
}
 

Затем я вижу в main func, что есть проверка, если err это nil так, если я правильно понимаю error nil , в некоторых ситуациях вполне возможно, что функция возвращается. Таким образом MyError , struct может принимать nil значения. Но тогда, если я попытаюсь скомпилировать это:

 import (
    "fmt"
    "time"
)

type MyError struct {
    When time.Time
    What string
}

func (e MyError) Error() string {
    return fmt.Sprintf("at %v, %s",
        e.When, e.What)
}

func run() error {
    return nil
    return MyError{
        time.Now(),
        "it didn't work",
    }
}

func main() {
    var err2 MyError = nil

    fmt.Println(err2)
    if err := run(); err != nil {
        fmt.Println(err)
    }
}
 

компилятор go сказал, что: prog.go:27: cannot use nil as type MyError in assignment
[process exited with non-zero status]

Почему в верхнем регистре компиляция проходит успешно, а в этом случае компиляция завершается неудачно? Возможно ли, чтобы структуры были nil (я думаю, нет, но тогда почему run функция компилируется?)

Ответ №1:

В первом примере *MyError реализован error интерфейс. Как вы можете видеть, это указатель, а указатель может иметь nil значение.

 var err *MyError
err == nil // true

var err *MyError = new(MyError)
err == nil // false
 

Но во втором примере это MyError то, что реализует error интерфейс, и это больше не указатель.

 var err MyError
err == MyError{} // true
amp;err == nil // false
err == nil // Compilation error
 

Это может быть адрес err этого времени nil , а не сама переменная.


В качестве сравнения рассмотрим int тип: var i int . Вы можете проверить i == 0 , например, но было бы ошибкой проверять, если i == nil , потому nil что не является целым числом (точно так же, как это не было MyError раньше). Но вы все равно можете проверить, является ли адрес i nil : amp;i == nil .


Редактировать

Имейте в виду, что эта функция всегда будет возвращаться nil (выполнение останавливается сразу после первого return ):

 func run() error {
    return nil
    return MyError{
        time.Now(),
        "it didn't work",
    }
}
 

Он компилируется, потому что прототип функции сообщает, что она должна возвращать значение an error и действительно nil является допустимым error , как и MyError{} переменная. Но попробуйте изменить этот прототип функции на этот:

 func run() MyError
 

Вы увидите, что компиляция завершается с ошибкой, потому nil что не является MyError переменной, даже если это допустимое значение для error типа.

Комментарии:

1. Я понимаю это, но посмотрите на run функцию. Он возвращает MyError, но также может возвращать nil .

2. Ага, теперь это имеет смысл. Спасибо вам. 🙂

Ответ №2:

Интерфейс с нулевым значением — это не то же самое, что интерфейс, содержащий нулевое значение.

В вашем вопросе вы видели, что интерфейс (в вашем случае встроенный интерфейс ошибок) может быть nil , и сделали неверный вывод о том, что каждый тип, который может реализовать интерфейс, также может быть nil.