Menu
Coddy logo textTech

Интерфейс error

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 52 из 107.

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

type error interface {
    Error() string
}

Любой тип, реализующий метод Error(), возвращающий строку, автоматически удовлетворяет интерфейсу error. Это работа системы интерфейсов Go — явного объявления не требуется.

Самый распространенный способ создания ошибок — использование пакета errors:

import "errors"

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    return a / b, nil
}

Функции в Go обычно возвращают ошибку в качестве последнего возвращаемого значения. Если ошибка не возникла, возвращается nil. Затем вызывающая сторона проверяет, не является ли ошибка отличной от nil, прежде чем продолжить:

result, err := Divide(10, 0)
if err != nil {
    fmt.Println("Error:", err.Error())
    return
}
fmt.Println("Result:", result)

Вы также можете использовать fmt.Errorf для создания отформатированных сообщений об ошибках:

func GetUser(id int) (string, error) {
    if id <= 0 {
        return "", fmt.Errorf("invalid user id: %d", id)
    }
    return "Alice", nil
}

Этот паттерн возврата ошибок вместо генерации исключений является фундаментальным для Go. Это делает обработку ошибок явной и заставляет вас задумываться о том, что происходит, когда что-то идет не так — ключевой аспект написания надежного объектно-ориентированного кода.

challenge icon

Задание

Легко

Давайте создадим систему банковских счетов, которая демонстрирует паттерн обработки ошибок в Go. Вы создадите простой счет, который проверяет операции и возвращает информативные ошибки, когда что-то идет не так.

Вы организуете свой код в двух файлах:

  • account.go: Создайте структуру BankAccount с экспортируемым полем Owner (string) и неэкспортируемым полем balance (float64). Реализуйте следующее:
    • NewBankAccount(owner string, initialDeposit float64) (*BankAccount, error) — конструктор, который возвращает ошибку с сообщением "initial deposit must be positive", если начальный депозит равен нулю или отрицателен. В противном случае создайте и верните счет.
    • Deposit(amount float64) error — добавляет деньги на баланс. Возвращает ошибку с сообщением "deposit amount must be positive", если сумма равна нулю или отрицательна.
    • Withdraw(amount float64) error — снимает деньги с баланса. Возвращает ошибку с сообщением "withdrawal amount must be positive", если сумма равна нулю или отрицательна, или "insufficient funds", если баланс меньше суммы снятия.
    • Balance() float64 — возвращает текущий баланс.
  • main.go: Считайте данные счета и суммы транзакций из входных данных. Попытайтесь создать счет и выполнить операции. После каждой операции выводите либо сообщение об успехе, либо сообщение об ошибке.

Будут предоставлены следующие входные данные:

  • Строка 1: Имя владельца (Owner name)
  • Строка 2: Сумма начального депозита
  • Строка 3: Сумма депозита
  • Строка 4: Сумма снятия

Для каждой операции выведите результат:

  • После создания счета: Account created for [Owner] with balance: $[balance] или Error: [error message]
  • После депозита: Deposited successfully. New balance: $[balance] или Error: [error message]
  • После снятия: Withdrew successfully. New balance: $[balance] или Error: [error message]

Форматируйте все значения баланса с двумя знаками после запятой. Если создание счета не удалось, пропустите остальные операции.

Например, для входных данных Alice, 100, 50 и 200 ваш вывод должен быть таким:

Account created for Alice with balance: $100.00
Deposited successfully. New balance: $150.00
Error: insufficient funds

А для Bob, -50, 25 и 10 ваш вывод должен быть таким:

Error: initial deposit must be positive

Шпаргалка

Go обрабатывает ошибки через встроенный интерфейс error, который требует наличия одного метода:

type error interface {
    Error() string
}

Создавайте ошибки с помощью пакета errors:

import "errors"

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    return a / b, nil
}

Функции возвращают ошибки в качестве последнего возвращаемого значения. Возвращайте nil, если ошибка не возникла:

result, err := Divide(10, 0)
if err != nil {
    fmt.Println("Error:", err.Error())
    return
}
fmt.Println("Result:", result)

Используйте fmt.Errorf для форматированных сообщений об ошибках:

func GetUser(id int) (string, error) {
    if id <= 0 {
        return "", fmt.Errorf("invalid user id: %d", id)
    }
    return "Alice", nil
}

Попробуйте сами

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Чтение имени владельца
	ownerName, _ := reader.ReadString('\n')
	ownerName = strings.TrimSpace(ownerName)

	// Чтение начального депозита
	initialDepositStr, _ := reader.ReadString('\n')
	initialDeposit, _ := strconv.ParseFloat(strings.TrimSpace(initialDepositStr), 64)

	// Чтение суммы депозита
	depositStr, _ := reader.ReadString('\n')
	depositAmount, _ := strconv.ParseFloat(strings.TrimSpace(depositStr), 64)

	// Чтение суммы снятия
	withdrawStr, _ := reader.ReadString('\n')
	withdrawAmount, _ := strconv.ParseFloat(strings.TrimSpace(withdrawStr), 64)

	// TODO: Создайте новый банковский счет, используя NewBankAccount
	// Если возникла ошибка, выведите "Error: [error message]" и выполните return
	// В случае успеха выведите "Account created for [Owner] with balance: $[balance]"

	// TODO: Попробуйте внести сумму депозита
	// Если возникла ошибка, выведите "Error: [error message]"
	// В случае успеха выведите "Deposited successfully. New balance: $[balance]"

	// TODO: Попробуйте снять сумму для снятия
	// Если возникла ошибка, выведите "Error: [error message]"
	// В случае успеха выведите "Withdrew successfully. New balance: $[balance]"

	// Не забудьте отформатировать значения баланса с двумя знаками после запятой, используя fmt.Printf("%.2f", balance)

	_ = ownerName
	_ = initialDeposit
	_ = depositAmount
	_ = withdrawAmount
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование