Скука и рутина это зло

run{}

Как ограничить набор типов реализующих интерфейс в Go?

Когда есть свободное время я разбираюсь с языком Go, пытаюсь разобраться с его внутренностями, пытаюсь писать какие-то проекты с его помощью.

В Go вводится ряд новшеств, которых нет в других хорошо развитых и популярных языках. Соответственно, код на Go не похож на код на Java или C++, слишком уж разный набор фич, совсем другие средства выражения.

Наткнулся на прикольный способ искусственно ограничить список типов удовлетворяющих интерфейсу.

Ограничение набора типов реализующих конкретный интерфейс

К примеру если хочется использовать в своей FooFunc функции параметр a, так, чтобы a мог быть разных типов, но при этом нет одного интерфейса их объединяющего, с помощью которого можно было бы абстрагироваться. В таких случаях используют тип interface{}

func FooFunc(a interface{}) {
    // do what ever you want
}

Но так как как функция публичная, видимая извне, то может оказаться так, что в качестве a передадут не то значение что ожидает ваш код.

Как раз на этот случай есть следующий трюк, можно объявить специальный интерфейс, который требует реализации лишь одного приватного метода-заглушки. Далее можно объявить методы для типов которые хочется использовать, тогда тип a будет проверяться на стадии компиляции.

type aTypeInterface interface {
    aMethod()
}

func FooFunc(a aTypeInterface) {
    // do what ever you want
}

func (bType) aMethod(){}
func (cType) aMethod(){}
func (dType) aMethod(){}

Теперь параметр a в функции FooFunc может быть лишь одним из указанных типов: bType, cType, dType, и это проверяется на стадии компиляции.

Наткнулся на такой код в пакете стандартной библиотеки go/ast/ast.go:

/* ... skipped ... */

// All node types implement the Node interface.
type Node interface {
    Pos() token.Pos // position of first character belonging to the node
    End() token.Pos // position of first character immediately after the node
}

// All statement nodes implement the Stmt interface.
type Stmt interface {
    Node
    stmtNode()
}

/* ... skipped ... */

// stmtNode() ensures that only statement nodes can be
// assigned to a StmtNode.
//
func (*BadStmt) stmtNode()        {}
func (*DeclStmt) stmtNode()       {}
func (*EmptyStmt) stmtNode()      {}
func (*LabeledStmt) stmtNode()    {}
func (*ExprStmt) stmtNode()       {}
func (*SendStmt) stmtNode()       {}
func (*IncDecStmt) stmtNode()     {}
func (*AssignStmt) stmtNode()     {}
func (*GoStmt) stmtNode()         {}
func (*DeferStmt) stmtNode()      {}
func (*ReturnStmt) stmtNode()     {}
func (*BranchStmt) stmtNode()     {}
func (*BlockStmt) stmtNode()      {}
func (*IfStmt) stmtNode()         {}
func (*CaseClause) stmtNode()     {}
func (*SwitchStmt) stmtNode()     {}
func (*TypeSwitchStmt) stmtNode() {}
func (*CommClause) stmtNode()     {}
func (*SelectStmt) stmtNode()     {}
func (*ForStmt) stmtNode()        {}
func (*RangeStmt) stmtNode()      {}

Пример именно такого подхода

Comments