Ответ
Ответ не найден
Ответ 18+ 🔞
А, ну ты глянь, опять эти ваши интерфейсы с кучей методов, которые нихуя не нужны обычному пользователю. Представь, что ты купил микроволновку, а там, кроме кнопки «разогреть», ещё двадцать режимов: «разморозить арктического окуня», «подогреть с угрызениями совести» и «слегка тёпленький, как пощёчина». Кто этим пользуется? Никто, блядь.
Вот и с ReadCloser та же история. В 99% случаев тебе от него нужно только одно: прочитать данные и закрыться. А он тебе, сука, помимо Read, ещё и Close подсовывает. Ну окей, закрыть — это логично, ресурсы освободить. Но зачем мне, как пользователю, об этом думать? Я ж не сантехник, чтобы каждый раз после чтения файла крутить вентили!
Поэтому и придумали io.Reader. Это как та самая кнопка «разогреть». Один метод — Read. Всё, пиздец. Дали тебе поток байтов — читай, и не грузи мозг. А уж что там внутри — файл, сеть, или данные из жопы космического чебурашки — тебе похуй. Закроется оно само, когда нужно, или его кто-то выше закроет.
Смотри, вот пример, как это обычно выглядит в коде. Берём ReadCloser, но работаем с ним только как с Reader:
func processData(rc io.ReadCloser) error {
// Внутри функции нас интересует ТОЛЬКО чтение.
// Мы принимаем ReadCloser (например, от http.Response.Body),
// но используем только его часть — io.Reader.
data, err := io.ReadAll(rc) // ReadAll работает с io.Reader
if err != nil {
return err
}
// ... делаем что-то с data ...
return nil
// Закрытие rc — это ответственность ВЫЗЫВАЮЩЕГО кода.
// Не нашей функции. Мы тут просто читатели, блядь.
}
А вызывающий код уже сам решает, когда закрывать:
resp, err := http.Get("https://example.com")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close() // Вот он, Close! Но он ВНЕ функции processData.
err = processData(resp.Body)
if err != nil {
log.Fatal(err)
}
Суть в разделении ответственности, ёпта. Один код (processData) говорит: «Дай мне поток, я почитаю». Другой код (вызывающий) говорит: «На, читай. А закрыть я сам смогу, я не маленький». Это как снять пробу с борща, не вываливая весь котёл в тарелку. Повар (вызывающий код) дал тебе ложку (Reader), а котёл (ReadCloser) оставил у себя на плите, чтобы потом накрыть крышкой (Close).
Если бы processData требовал именно ReadCloser, это было бы как требовать от гостя: «Хочешь попробовать борщ — заходи на кухню и бери весь котёл, а потом сам его мой». Неудобно, блядь! Нарушается абстракция.
Поэтому io.Reader — это и есть та самая, правильная абстракция. Минимальная, необходимая и достаточная. Всё, что умеет давать данные, может быть Reader'ом. А то, что его ещё и закрыть можно — это уже детали реализации, которые нужны не всем. Так что запомни: когда пишешь функцию, всегда требуй минимально необходимый интерфейс. Хочешь читать — проси io.Reader. А если тебе вдруг понадобится закрывать — тогда да, io.ReadCloser. Но это уже особый случай, обычно на верхнем уровне. Не тащи эту обузу в каждую функцию, а то охуеешь потом от её тяжести.