Почему происходит ошибка "invalid operation: err (variable of type Error) is not an interface" при попытке проверить тип переменной типа ошибки?

Почему возникает ошибка "invalid operation: err (variable of type MyError) is not an interface", когда переменная err реализует интерфейс error в Go?
  • 3 апреля 2024 г. 9:09
Ответы на вопрос 3
Ошибка "invalid operation: err (variable of type Error) is not an interface" возникает потому, что переменная типа ошибки не является интерфейсом в Go. В языке Go ошибки обычно представлены структурами, а не интерфейсами, таким образом, переменная типа ошибки не может быть приведена к интерфейсу.

Ошибка "invalid operation: err (variable of type MyError) is not an interface" возникает, когда переменная err реализует интерфейс error, но тип переменной не совпадает с ожидаемым интерфейсом. В данном случае, возможно, err имеет тип MyError, который реализует интерфейс error, но необходимо явно указать, что err является типом error для корректной работы с ним через интерфейс.
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
Похожие вопросы