Это сайт — моя персональная записная книжка. Интересна мне, по большей части, история, своя жизнь и немного программирование.

Аналог defer в языке R

В Гоу есть интересная конструкция (defer) — её содержимое выполняется, когда заканчивает работу функция, в которой эта конструкция объявлена, очень удобно память освобождать или другие ресурсы. Я накидал небольшой пример, чтобы было понятно:

package main

import (
    "bufio"
    "fmt"
    "os"
)

func readFile(filename string) (out []string) {
    file, _ := os.Open(filename)
    defer file.Close()

    b := bufio.NewReader(file)

    for {
        if str, err := b.ReadString('\n'); err != nil {
            break
        } else {
            out = append(out, str)
        }
    }

    return out
}

func main() {
    var lines []string

    for _, line := range readFile("/etc/passwd") {
        if line[0] != '#' {
            lines = append(lines, line)
        }
    }

    fmt.Println(lines)
}

В примере одна функция построчно читает файл и возвращает массив строк. Её использует основная функция (main), чтобы прочитать файл с информацией о пользователях в системе, удалив оттуда комментарии — строки, начинающиеся с «#». Обработку ошибок я не стал делать — и без этого длинновато вышло.

В фунции чтения файла блок defer вызовется как только мы покинем функцию и закроет используемый файл. Такой способ лучше, чем отсутствующий «Гоу» блок try…finally, так как позволяет указать способ освобождения ресурса очень близко к началу его использования.

Есть языки, которые, на мой взгляд, решают эту задачу ещё лучше (например, Пайтон со своим with), но у Гоу своя атмосфера и выбранное решение отвечает духу языка.

«Эр» на «Гоу» совсем не похож, у языков совершенно разные идеологии, поэтому я очень удивился, обнаружив в «Эр» тот же подход, что выбран в «Гоу».Тот же пример на «Эре»:

readFile <- function(filename) {
    f <- file(filename, 'r')
    on.exit({
        close(f)
    })

    return(readLines(f))
}

lines <- readFile('/etc/passwd')
print(lines[!grepl('^#', lines)])

Конструкция on.exit делает тут ровно то же, что defer в «Гоу». Нельзя не заметить, что программа стала гораздо компактнее, но в многословности Гоу обвинить нельзя — всё-таки он более низкоуровневый, плюс статически типизированный, отсюда и более подробный код.

2 комментария
ufm 2017

её содержимое вычисляется, когда мы уходим из текущего блока выполнения

Не совсем так.
_Вычисляется_ — в момент определения, если это параметр функции.
_Выполняется_ когда выходим из _функции_ (а не блока).

как пример: https://play.golang.org/p/nh4zIotAFY

Евгений Степанищев (bolknote.ru) 2017

Комментарий для ufm:

Действительно, с терминологией у меня тут полный швах, сейчас поправлю.