@NkDev

Почему я получаю ошибку invalid operation: err (variable of type Error) is not an interface при проверке типа переменной?

Я пытаюсь проверить, реализует ли переменная err интерфейс error.
В данном коде видно что err является экземпляром MyError, который реализует метод Error.
Насколько я понимаю реализация метода Error является достаточным для того чтобы имплементировать интерфейс в Go.
Скажите почему я получаю ошибку?

package main

import "fmt"

type MyError struct {
	Message string
}

func (e MyError) Error() string {
	return e.Message
}

func main() {
	err := MyError{"Something went wrong"}

	// Проверяем, реализует ли err интерфейс error
	if _, ok := err.(error); ok {
		fmt.Println("err реализует интерфейс error")
	} else {
		fmt.Println("err НЕ реализует интерфейс error")
	}
}


Консольный вывод:

vlad@DESKTOP:~/go/src/project$ go run cmd/structure/main.go
# command-line-arguments                                                                          
cmd/structure/main.go:17:14: invalid operation: err (variable of type MyError) is not an interface


https://go.dev/play/p/AN2_V0ozTpL
  • Вопрос задан
  • 117 просмотров
Решения вопроса 2
https://go.dev/play/p/eDi3J3Zzcdg

package main

import "fmt"

type MyError struct {
	Message string
}

func (e MyError) Error() string {
	return e.Message
}

func (e MyError) Smth() {
}

type SomeInterface interface {
	Smth()
}

func main() {
	var err SomeInterface
	err = MyError{"Something went wrong"}

	// Проверяем, реализует ли err интерфейс error
	if _, ok := err.(error); ok {
		fmt.Println("err реализует интерфейс error")
	} else {
		fmt.Println("err НЕ реализует интерфейс error")
	}
}


У вас получилось, что тип переменной err это структура, но нужно чтобы тип был каким-нибудь интерфейсом.

В моем примере я создал интерфейс SomeInterface и добавил вашему типу метод, чтобы он этому интерфейсу удовлетворял. Теперь данный ассершн имеет смысл.

P.S. С пустым интерфейсом тоже работать будет https://go.dev/play/p/Yz0M1Wzopua
Ответ написан
Комментировать
Я полностью присоединюсь к ответу выше, просто хочу дополнить.

Замечу, что вы проверяете не то, что нужно, шиворот-навыворот. Проверять при приведении типов лучше конкретные типы. Т.е. мы получаем из функции ошибку в виде интерфейса error, а уже в проверке проверяем её на наш кастомный тип MyError. Поэтому желательно (не обязательно) не проверять переменную ошибки в той же функции, где вы её создали, а возвращать откуда-то и уже тогда проверять на ошибку.

К тому же, если у вас вопросы по этой теме, я настоятельно хочу порекомендовать правильно использовать кастомные типы ошибок, и даже если указатель на ваш кастомный тип ошиьбки равен nil, то ни в коем случае не возвращайте сам этот указатель, а возвращайте буквально nil. Так вы избавитесь от тысяч выстрелов себе в ногу.

package main

import (
	"fmt"
)

type MyError struct {
	Message string
}

func (e MyError) Error() string {
	return e.Message
}

func main() {
	// Возвращаем из функции нашу кастомную ошибку, но в виде интерфейса error
	err := foo()

	if err == nil {
		fmt.Println("Нет ошибки")
	// И теперь тут приводим error к нашему типу MyError и проверяем
	} else if myErr, ok := err.(MyError); ok {
		fmt.Printf("Ура! Нужный нам тип ошибки: %v\n", myErr.Message)
	} else {
		fmt.Println("Какой-то другой тип ошибки:", err)
	}

	// Проверка одной из "подстав" Go

	err = bad()
	if err != nil {
		fmt.Println("Упс... Как так... Не nil...")
	} else {
		fmt.Println("Должно вывестись это, но не выводится...")
	}

	err = good()
	if err != nil {
		fmt.Println("Это не должно выводиться, всё верно.")
	} else {
		fmt.Println("Ошибки нет, всё верно.")
	}
}

func foo() error {
	err := MyError{"Ой! Ошибка MyError!"}
	// err := fmt.Errorf("Ой! Обычная ошибка!")
	// var err error = nil
	return err
}

func bad() error {
	var p *MyError = nil // Вроде же nil, но не работает....
	// p = &MyError{"Ой!"} // Пробуем создать ошибку, и всё работает.

	if p == nil {
		fmt.Println("Ну nil же-ж... Должно же-ж работать", p)
	}

	return p
}

func good() error {
	// return MyError{"Ой!"}

        // Буквально пишем "nil", никаких указателей, которые равны nil, это прямой выстрел в ногу
	return nil
}


https://go.dev/play/p/2YcWcH9oqel
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы