Подтвердить что ты не робот

Как добавить новые методы в существующий тип в Go?

Я хочу добавить удобный утилитарный метод для gorilla/mux Route и Router:

package util

import(
    "net/http"
    "github.com/0xor1/gorillaseed/src/server/lib/mux"
)

func (r *mux.Route) Subroute(tpl string, h http.Handler) *mux.Route{
    return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}

func (r *mux.Router) Subroute(tpl string, h http.Handler) *mux.Route{
    return r.PathPrefix("/" + tpl).Subrouter().PathPrefix("/").Handler(h)
}

но компилятор сообщает мне

Невозможно определить новые методы для нелокального типа mux.Router

Так как бы мне этого добиться? Создать новый тип структуры с анонимными полями mux.Route и mux.Router? Или что-то другое?

4b9b3361

Ответ 1

Как упоминает компилятор, вы не можете распространять существующие типы в другом пакете. Вы можете определить свой собственный псевдоним или подпакет следующим образом:

type MyRouter mux.Router

func (m *MyRouter) F() { ... }

или путем встраивания исходного маршрутизатора:

type MyRouter struct {
    *mux.Router
}

func (m *MyRouter) F() { ... }

...
r := &MyRouter{router}
r.F()

Ответ 2

Я хотел бы расширить ответ, данный @jimt здесь. Этот ответ правильный и очень помог мне разобраться в этом. Однако у обоих методов есть некоторые оговорки (псевдоним, вставка), с которыми у меня возникли проблемы.

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

Метод 1 - определение типа

type child parent
// or
type MyThing imported.Thing
  • Предоставляет доступ к полям.
  • Не предоставляет доступ к методам.

Способ 2. Встраивание (официальная документация)

type child struct {
    parent
}
// or with import and pointer
type MyThing struct {
    *imported.Thing
}
  • Предоставляет доступ к полям.
  • Предоставляет доступ к методам.
  • Требуется рассмотрение для инициализации.

Резюме

  • При использовании метода композиции встроенный родительский объект не будет инициализирован, если он является указателем. Родитель должен быть инициализирован отдельно.
  • Если встроенный родительский элемент является указателем и не инициализируется при инициализации дочернего элемента, произойдет ошибка разыменования нулевого указателя.
  • И определение типа, и случаи встраивания предоставляют доступ к полям родительского элемента.
  • Определение типа не разрешает доступ к родительским методам, но встраивание родительского делает.

Вы можете увидеть это в следующем коде.

рабочий пример на детской площадке

package main

import (
    "fmt"
)

type parent struct {
    attr string
}

type childAlias parent

type childObjParent struct {
    parent
}

type childPointerParent struct {
    *parent
}

func (p *parent) parentDo(s string) { fmt.Println(s) }
func (c *childAlias) childAliasDo(s string) { fmt.Println(s) }
func (c *childObjParent) childObjParentDo(s string) { fmt.Println(s) }
func (c *childPointerParent) childPointerParentDo(s string) { fmt.Println(s) }

func main() {
    p := &parent{"pAttr"}
    c1 := &childAlias{"cAliasAttr"}
    c2 := &childObjParent{}
    // When the parent is a pointer it must be initialized.
    // Otherwise, we get a nil pointer error when trying to set the attr.
    c3 := &childPointerParent{}
    c4 := &childPointerParent{&parent{}}

    c2.attr = "cObjParentAttr"
    // c3.attr = "cPointerParentAttr" // NOGO nil pointer dereference
    c4.attr = "cPointerParentAttr"

    // CAN do because we inherit parent fields
    fmt.Println(p.attr)
    fmt.Println(c1.attr)
    fmt.Println(c2.attr)
    fmt.Println(c4.attr)

    p.parentDo("called parentDo on parent")
    c1.childAliasDo("called childAliasDo on ChildAlias")
    c2.childObjParentDo("called childObjParentDo on ChildObjParent")
    c3.childPointerParentDo("called childPointerParentDo on ChildPointerParent")
    c4.childPointerParentDo("called childPointerParentDo on ChildPointerParent")

    // CANNOT do because we don't inherit parent methods
    // c1.parentDo("called parentDo on childAlias") // NOGO c1.parentDo undefined

    // CAN do because we inherit the parent methods
    c2.parentDo("called parentDo on childObjParent")
    c3.parentDo("called parentDo on childPointerParent")
    c4.parentDo("called parentDo on childPointerParent")
}