Интерфейс io.Closer

Интерфейс io.Closer определяет базовый метод Close, который используется для освобождения ресурсов, связанных с объектом. В первом параметре метод не принимает аргументов, а возвращает ошибку типа error, которая указывает на успешность закрытия ресурса.

Метод Close обычно вызывается после завершения работы с ресурсом, чтобы избежать утечек памяти и освободить системные дескрипторы. Многие типы в стандартной библиотеке Go, такие как os.File, net.Conn и http.Response, реализуют этот интерфейс.

Важно отметить, что вызов Close должен быть безопасным для многократного использования: повторный вызов не должен приводить к панике, а возвращать ошибку os.ErrClosed или подобную. Также рекомендуется использовать конструкцию defer для гарантированного закрытия ресурса.

Синтаксис

gotype Closer interface {
  Close() error
}

Пример с файлом

Рассмотрим пример использования io.Closer с открытым файлом. Функция os.Open возвращает файл, который реализует интерфейс io.Closer:

gopackage main

import (
  "fmt"
  "io"
  "os"
)

func main() {
  file, err := os.Open("example.txt")
  if err != nil {
    fmt.Println("Error opening file:", err)
    return
  }
  defer file.Close()

  data := make([]byte, 100)
  n, err := file.Read(data)
  if err != nil && err != io.EOF {
    fmt.Println("Error reading file:", err)
    return
  }

  fmt.Printf("Read %d bytes: %s", n, string(data[:n]))
}

Результат выполнения кода (при наличии файла example.txt):

go"Read 13 bytes: Hello, World!"

Пример с HTTP-ответом

В этом примере показано, как закрыть тело HTTP-ответа, которое реализует io.Closer:

gopackage main

import (
  "fmt"
  "io"
  "net/http"
)

func main() {
  resp, err := http.Get("https://example.com")
  if err != nil {
    fmt.Println("Error making request:", err)
    return
  }
  defer resp.Body.Close()

  body, err := io.ReadAll(resp.Body)
  if err != nil {
    fmt.Println("Error reading body:", err)
    return
  }

  fmt.Printf("Response status: %s\n", resp.Status)
  fmt.Printf("Body length: %d bytes", len(body))
}

Результат выполнения кода:

go"Response status: 200 OK"
"Body length: 1256 bytes"

Пример с функцией NopCloser

В стандартной библиотеке есть функция io.NopCloser, которая оборачивает io.Reader и возвращает io.ReadCloser с пустой реализацией Close. Это полезно, когда требуется интерфейс io.Closer, но закрывать ничего не нужно:

gopackage main

import (
  "fmt"
  "io"
  "strings"
)

func main() {
  r := strings.NewReader("example data")
  rc := io.NopCloser(r)
  defer rc.Close()

  data, err := io.ReadAll(rc)
  if err != nil {
    fmt.Println("Error reading:", err)
    return
  }

  fmt.Printf("Data: %s", string(data))
}

Результат выполнения кода:

go"Data: example data"

Смотрите также

  • интерфейс io.ReadCloser,
    который объединяет io.Reader и io.Closer
  • интерфейс io.WriteCloser,
    который объединяет io.Writer и io.Closer
  • функцию io.NopCloser,
    которая создает ReadCloser без операции закрытия
  • интерфейс os.File,
    который реализует io.Closer для работы с файлами