英文:
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)
然后,这个Handle
结构体作为接收器参数传递给需要该handle
的函数:
func (h *Handle) LinkList() ([]Link, error)
我对Go语言还不熟悉,不确定如何将它们联系在一起。我卡在了这里:
func (h *Handle) showInts() {
ints, err := h.netlink.LinkList()
if err != nil {
log.Fatal(err)
}
for i, r := range ints {
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()
}
英文:
currently working with the vishvananda/netns
package trying to extract routes from a specific network namespace.
There is a defined Handle
struct which is returned when I request a 'handle' for a specific network namespace. As such:
func NewHandleAt(ns netns.NsHandle, nlFamilies ...int) (*Handle, error)
This is then a receiver argument (?) to a function that requires that handle,
func (h *Handle) LinkList() ([]Link, error)
I'm new to go and not sure how to tie these together. I'm stuck with:
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()
}
答案1
得分: 7
更新
在撰写原始答案时,涉及了许多内容,没有明确的结构,所以这里有一个更有结构的版本:
根据你实际提问的是什么(例如“如何向导出的类型添加一个接收器函数/方法”或“到底什么是接收器函数”),答案如下:
如何向导出的类型添加一个接收器函数?
很简单,就像你对任何其他类型做的一样。实际上,你已经接近了。这样是不行的:
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
返回(*Handle, error)
,而netlink.Handle
是一个接收器参数,正确的方式应该是:
var mh *MyHandle
if h, err := netlink.NewHandleAt(pid); err != nil {
log.Fatal(err) // 出错了
} else {
mh = (*MyHandle)(h)
}
mh.showInts() // 在类型为*MyHandle的mh上调用showInts方法
将外部类型“包装”在自定义类型中意味着你会经常进行类型转换。假设`netlink.Handle`有一个`Test`方法,你想在`showInts`内部调用它:
```go
func (h *MyHandle) showInts() {
nh := (*netlink.Handle)(h) // 需要进行类型转换
nh.Test()
}
我还会将变量名从pid
改为nsh
或其他什么,因为它是一个NsHandle
,而不是一个pid
...
什么是接收器参数?
因为你写了这个:
这是一个接收器参数(?),用于一个需要该句柄的函数,
我觉得你对接收器参数不是完全清楚。简单来说,它就像一个函数参数,但不是只传递给函数的参数,而是保存了调用该函数的对象/值的参数。基本上,它是函数/方法被调用的“实例”。可以将其视为许多面向对象编程语言中的this
关键字:
func (h *MyHandle) showInts() {
return
}
在类似C++的语言中,它可能是这样的:
class MyHandle : Handle
{
public:
void showInts(void) { return; } // 将h替换为this
}
然而,有一些重要的区别:
- 接收器参数可以是指针,也可以是值 - 对于值接收器,方法不能修改接收器的值
- 没有私有、公有或受保护的概念...至少不是传统的面向对象方式
- ...
有很多不同之处,也许考虑一下学习一下Go语言的基础知识。关于Go方法的内容可以在这里找到。
其他问题/奇怪的事情
再次查看你的代码后,我真的不确定这个是正确的:
h.netlink.LinkList()
在你的main
函数中,你调用了netlink.LinkList()
。h
是一个*netlink.Handler
。如果你需要调用netlink.LinkList
函数,很可能h.netlink.LinkList
不是你想要做的。相反,你应该简单地调用netlink.LinkList()
。
这是假设你需要首先调用该函数。
既然你已经在main
函数中调用了它,为什么不将其作为参数传递呢?
// 在main函数中:
ints, err := netlink.LinkList()
// ...
h.showInts(ints)
func (h *MyHandle)showInts(ll []netlink.Link) {
}
英文:
Update
While writing the original answer, touched on a number of things, without any clear structure, so here's a more structured version:
Depending on what you're actually asking (ie "How do I add a receiver function/method to an exported type", or "What the hell is a receiver function"), the answers are as follows:
How do I add a receiver function to an exported type?
Easy, same as you do with any other type. You were close, in fact. This doesn't work:
func (h *Handler) showInts() {}
Because you're adding a method to the Handler
type in your package. Given you have a main
function, that would be the main
package. You're trying to add it to the netlink.Handler
type instead. In which case, this will work:
func (h *netlink.Handler) showInts(){}
The type is netlink.Handler
in your main package after all... This, however will not work. The compiler will refuse to compile, telling you: "Cannot define new methods on non-local type". This is easily mitigated, though, by creating a new type, and add the method there:
type MyHandler netlink.Handler
func (h *MyHandler) showInts(){}
Be that as it may, the last 2 lines in your code strike me as wrong.
Given that NewHandleAt
returns (*Handle, error)
, and netlink.Handle
is a receiver argument, the correct way would be:
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
The fact that you've "wrapped" the external type in a custom type does mean you'll find yourself casting the same thing quite a lot. Say netlink.Handle
has a Test
method, and you want to call it inside showInts
:
func (h *MyHandle) showInts() {
nh := (*netlink.Handle)(h) //cast required
nh.Test()
}
I'd also change the varname from pid
to nsh
or something, because it's a NsHandle
, and not a pid
after all...
What is a receiver argument?
Because you wrote this:
> This is then a receiver argument (?) to a function that requires that handle,
I get the impression you're not entirely clear on what a receiver argument is. Put simply, it's like a function argument, but instead of an argument that is just passed to a function, it's an argument that holds the object/value on which the function is called. Basically, it's the "instance" on which the function/method is called. Think of it as the this
keyword in many OOP languages:
func (h *MyHandle) showInts() {
return
}
In something like C++ would be
class MyHandle : Handle
{
public:
void showInts(void) { return; } // replace h with this
}
There are significant differences, however:
- The receiver argument can be a pointer, or a value - in case of a value receiver, the method cannot modify the receiver value
- There's no such thing as private, public, or protected... at least not in the traditional OO way
- ...
There's quite a few differences, perhaps consider going through the golang tour. The stuff about go methods can be found here
Other issues/weird things
After looking at your code again, I'm really not sure whether this is correct:
h.netlink.LinkList()
In your main
function, you call netlink.LinkList()
. h
is a *netlink.Handler
. If you need to call the netlink.LinkList
function, it's highly likely h.netlink.LinkList
is not what you want to do. Instead, you should simply call netlink.LinkList()
.<br>
That's assuming you need to call the function in the first place.
Given that you've already called it in the main
function, why not pass it as an argument?
//in main:
ints, err := netlink.LinkList()
//...
h.showInts(ints)
func (h *MyHandle)showInts(ll []netlink.Link) {
}
答案2
得分: 1
感谢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()
}
英文:
Thanks Elias, awesome answer!
From that, I've written the following code which will list interfaces belonging to a specific namespace. Thanks!
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()
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论