← Назад к вопросам

Какие знаешь способы обработать ошибки?

1.3 Junior🔥 251 комментариев
#Основы Go

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы обработки ошибок в Go

Go имеет несколько встроенных и практических способов обработки ошибок. В отличие от других языков, Go не использует исключения, предпочитая явное управление ошибками.

1. Традиционное возвращение ошибки

Самый распространённый способ в Go — возвращать error как последнее значение из функции:

func ReadFile(filename string) ([]byte, error) {
    file, err := os.Open(filename)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    data, err := io.ReadAll(file)
    if err != nil {
        return nil, fmt.Errorf("failed to read file: %w", err)
    }
    
    return data, nil
}

func main() {
    data, err := ReadFile("file.txt")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(data))
}

2. Обёртывание ошибок с контекстом

import "fmt"

func ProcessData(data []byte) error {
    if len(data) == 0 {
        return fmt.Errorf("empty data: %w", io.ErrUnexpectedEOF)
    }
    // ...
    return nil
}

func main() {
    err := ProcessData([]byte{})
    if err != nil {
        fmt.Printf("Error: %v\n", err) // Error: empty data: unexpected EOF
    }
}

3. Проверка типа ошибки (Type Assertion)

func HandleNetworkError(err error) {
    switch err := err.(type) {
    case *net.OpError:
        fmt.Println("Network error:", err.Op)
    case *os.PathError:
        fmt.Println("Path error:", err.Path)
    default:
        fmt.Println("Unknown error:", err)
    }
}

func main() {
    _, err := net.Dial("tcp", "invalid:999")
    if err != nil {
        HandleNetworkError(err)
    }
}

4. Проверка интерфейса ошибки (errors.As)

import "errors"

type ValidationError struct {
    Field   string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}

func ValidateEmail(email string) error {
    if !strings.Contains(email, "@") {
        return ValidationError{Field: "email", Message: "invalid format"}
    }
    return nil
}

func main() {
    err := ValidateEmail("invalid")
    if err != nil {
        var ve ValidationError
        if errors.As(err, &ve) {
            fmt.Printf("Field: %s, Message: %s\n", ve.Field, ve.Message)
        }
    }
}

5. Проверка значения ошибки (errors.Is)

import (
    "errors"
    "os"
)

var ErrNotFound = errors.New("resource not found")

func GetResource(id string) (string, error) {
    if id == "" {
        return "", ErrNotFound
    }
    return "resource", nil
}

func main() {
    _, err := GetResource("")
    if errors.Is(err, ErrNotFound) {
        fmt.Println("Resource doesn't exist")
    }
    
    // Также работает с ошибками из стандартной библиотеки
    _, err = os.Open("nonexistent.txt")
    if errors.Is(err, os.ErrNotExist) {
        fmt.Println("File not found")
    }
}

6. Panic и Recover (для критических ошибок)

func SafeOperation() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    panic("Something went wrong!") // Выбросить panic
}

func main() {
    SafeOperation()
    fmt.Println("Program continues") // Выполнится, несмотря на panic
}

7. Sentinel Ошибки (Sentinel Errors)

var (
    ErrEmptyInput   = errors.New("input cannot be empty")
    ErrInvalidInput = errors.New("input format is invalid")
    ErrTimeout      = errors.New("operation timed out")
)

func Process(input string) error {
    if input == "" {
        return ErrEmptyInput
    }
    if len(input) > 1000 {
        return ErrInvalidInput
    }
    return nil
}

func main() {
    err := Process("")
    if err == ErrEmptyInput {
        fmt.Println("Empty input provided")
    }
}

8. Кастомный тип ошибки

type AppError struct {
    Code    int
    Message string
    Details error
}

func (e AppError) Error() string {
    return fmt.Sprintf("[%d] %s: %v", e.Code, e.Message, e.Details)
}

func APICall() error {
    return AppError{
        Code:    500,
        Message: "Internal Server Error",
        Details: errors.New("database connection failed"),
    }
}

func main() {
    err := APICall()
    fmt.Println(err) // [500] Internal Server Error: database connection failed
}

9. Error wrapping с контекстом (pkg/errors)

import "github.com/pkg/errors"

func ReadConfig(path string) (Config, error) {
    data, err := ioutil.ReadFile(path)
    if err != nil {
        return Config{}, errors.Wrap(err, "failed to read config file")
    }
    return parseConfig(data)
}

func main() {
    _, err := ReadConfig("config.json")
    if err != nil {
        fmt.Printf("%+v\n", err) // Выведет полный stack trace
    }
}

10. Мультиошибки (Go 1.20+)

import "errors"

func ValidateUser(user User) error {
    var errs []error
    
    if user.Name == "" {
        errs = append(errs, errors.New("name is required"))
    }
    if user.Email == "" {
        errs = append(errs, errors.New("email is required"))
    }
    if user.Age < 0 {
        errs = append(errs, errors.New("age cannot be negative"))
    }
    
    if len(errs) > 0 {
        return errors.Join(errs...) // Объединить несколько ошибок
    }
    
    return nil
}

func main() {
    user := User{Age: -5}
    err := ValidateUser(user)
    if err != nil {
        fmt.Println(err)
        // name is required
        // email is required
        // age cannot be negative
    }
}

Best Practices

  • Всегда проверяй ошибкиif err != nil должна быть везде
  • Не игнорируй ошибки — используй _ = только если точно уверен
  • Добавляй контекст — используй fmt.Errorf("operation: %w", err)
  • Используй sentinel ошибки для известных случаев
  • Создавай кастомные типы для специфичных ошибок, которые нужно обрабатывать
  • Не используй panic для обычных ошибок — только для невосстанавливаемых ситуаций
  • Логируй с контекстом — используй structured logging

Философия Go — явное управление ошибками вместо неявных исключений, что делает код более предсказуемым и надёжным.

Какие знаешь способы обработать ошибки? | PrepBro