Удобнее на ты или на вы?

Ответ

Ответ не найден

Ответ 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. Но это уже особый случай, обычно на верхнем уровне. Не тащи эту обузу в каждую функцию, а то охуеешь потом от её тяжести.