Метод-значение

Допустим, у нас есть сервер, который можно тыкать палочкой:

type Server struct {
    nPings int
}

func (s *Server) Ping() {
    s.nPings++
}

Давайте попингуем его вот так:

s := Server{}
ping := s.Ping // хм

ping()
ping()
ping()

Как думаете, что произойдет? Я задал этот вопрос на канале и получил такие ответы:

Что будет при таком вызове ping?

18% Ругань компилятора
    ■■■■

11% Паника в рантайме
    ■■

15% Паника у меня
    ■■■

56% Увеличится s.nPings
    ■■■■■■■■■■■

Действительно, метод у значения структуры (или указателя на значение) — это просто функция с конкретным получателем (тем самым значением структуры). Такой метод-значение (method value) можно использовать как обычную функцию — вызывать напрямую или передавать в качестве параметра, например.

Предположим, у нас есть тип-монитор, который умеет периодически проверять доступность сервиса, ведет историю проверок, считает процент доступности и выполняет другие полезные действия.

Монитору совершенно не нужно знать о конкретной реализации сервиса. Достаточно получить в конструкторе функцию-пинг, и дальше вызывать ее:

type Monitor struct {
    ping func() error
    // ...
}

func NewMonitor(ping func() error) *Monitor {
    return &Monitor{ping}
}

И если у нашего сервера есть подходящий по сигнатуре метод:

func (s *Server) Ping() error {
    // ...
}

То можно прямо его и передать без всяких оберток:

s := Server{}
m := NewMonitor(s.Ping)

Такие вот элементы функционального программирования в глубоко процедурном языке :)

★ Подписывайтесь на новые заметки.