Комментарии (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 — явное управление ошибками вместо неявных исключений, что делает код более предсказуемым и надёжным.