在导入包中定义结构时如何使用 go receiver

How to use go receiver when struct is defined in imported package

目前正在使用 vishvananda/netns 包尝试从特定网络名称空间中提取路由。

有一个已定义的 Handle 结构,当我为特定网络名称空间请求 'handle' 时返回该结构。因此:

func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error)

这就是需要该句柄的函数的接收者参数 (?),

func (h *Handle) LinkList() ([]Link, error)

我是新手,不确定如何将它们联系在一起。我坚持:

func (h *Handle) showInts() {
  int, err := h.netlink.LinkList()
  if err != nil {
      log.Fatal(err)
  }

  for i, r := range int {
      log.Printf("%d: %s", i, r.Attrs().Name)
  }
}


func main() {
    ints, err := netlink.LinkList()
    if err != nil {
        log.Fatal(err)
    }
    for i, r := range ints {
        log.Printf("%d: %s", i, r.Attrs().Name)
    }

    pid, err := netns.GetFromPid(9097)
    if err != nil {
        log.Fatal(err)
    }

    netlink.NewHandleAt(pid)
    showInts()
}

更新

在写原答案时,触及了一些东西,没有任何清晰的结构,所以这里有一个更结构化的版本:

取决于您实际询问的内容(即 “如何将接收器 function/method 添加到导出类型”,或 ”接收函数是什么鬼),答案如下:

如何向导出类型添加接收函数?

简单,和其他类型一样。事实上,你很接近。这不起作用:

func (h *Handler) showInts() {}

因为您在 您的 包中的 Handler 类型中添加了一个方法。如果你有一个 main 函数,那就是 main 包。您正在尝试将其添加到 netlink.Handler 类型。在这种情况下,这将起作用:

func (h *netlink.Handler) showInts(){}

毕竟你的主包中的类型是 netlink.Handler...但是这 不会 工作。编译器会拒绝编译,告诉你:“不能在非本地类型上定义新方法”。不过,这很容易缓解,方法是创建一个新类型,并在其中添加方法:

type MyHandler netlink.Handler
func (h *MyHandler) showInts(){}

尽管如此,我觉得你代码中的最后两行是错误的。 鉴于 NewHandleAt returns (*Handle, error)netlink.Handle 是接收者参数,正确的方法是:

var mh *MyHandle
if h, err := netlink.NewHandleAt(pid); err != nil {
    log.Fatal(err) // something went wrong
} else {
    mh = (*MyHandle)(h)
}
mh.showInts() // call showInts on mh, which is of type *MyHandle

“包装” 自定义类型中的外部类型这一事实确实意味着您会发现自己经常转换相同的东西。假设 netlink.Handle 有一个 Test 方法,你想在 showInts:

中调用它
func (h *MyHandle) showInts() {
    nh := (*netlink.Handle)(h) //cast required
    nh.Test()
}

我还将变量名从 pid 更改为 nsh 或其他,因为它是 NsHandle,毕竟不是 pid...


什么是接收方参数?

因为你写了这个:

This is then a receiver argument (?) to a function that requires that handle,

我的印象是您并不完全清楚接收方参数是什么。简而言之,它就像一个函数参数,但它不是仅传递给函数的参数,而是一个保存调用函数的 object/value 的参数。基本上,它是调用 function/method 的 "实例"。将其视为许多 OOP 语言中的 this 关键字:

func (h *MyHandle) showInts() {
    return
}

在 C++ 之类的东西中会是

class MyHandle : Handle
{
    public:
        void showInts(void) { return; } // replace h with this
}

但是存在显着差异:

  • 接收者参数可以是指针或值 - 如果是值接收者,该方法不能修改接收者值
  • 不存在诸如私有、public 或受保护之类的东西...至少在传统的 OO 方式中没有
  • ...

差异还是挺多的,或许可以考虑一下golang之旅。关于 go 方法的东西可以找到 here


其他issues/weird件事

再看了一遍你的代码,我真的不确定这是否正确:

h.netlink.LinkList()

在您的 main 函数中,您调用了 netlink.LinkList()h 是一个 *netlink.Handler。如果您需要调用 netlink.LinkList 函数,很可能 h.netlink.LinkList 而不是 您想要执行的操作。相反,您应该简单地调用 netlink.LinkList().
这是假设您首先需要调用该函数。

鉴于您已经在 main 函数中调用了它,为什么不将其作为参数传递?

//in main:
ints, err := netlink.LinkList()
//...
h.showInts(ints)

func (h *MyHandle)showInts(ll []netlink.Link) {
}

谢谢 Elias,非常棒的回答!

据此,我编写了以下代码,其中将列出属于特定命名空间的接口。谢谢!

package main

import (
    "github.com/vishvananda/netns"
    "github.com/vishvananda/netlink"
    "log"
)

type NSHandle netlink.Handle

func (h *NSHandle) showInts() {
  nh := (*netlink.Handle)(h) //cast required
  int, err := nh.LinkList()
  if err != nil {
      log.Fatal(err)
  }

  log.Printf("Namespace Ints:")
  for i, r := range int {
      log.Printf("%d: %s", i, r.Attrs().Name)
  }
}

func getNSFromPID(pid int) (*NSHandle) {
  hpid, err := netns.GetFromPid(9115)
  if err != nil {
      log.Fatal(err)
  }
  var nsh *NSHandle
  if h, err := netlink.NewHandleAt(hpid); err != nil {
      log.Fatal(err) // something went wrong
  } else {
      nsh = (*NSHandle)(h)
  }
  return nsh
}

func main() {
  getNSFromPID(9115).showInts()
}