Golang方法函数中的接收者类型无效

huangapple go评论87阅读模式
英文:

Golang Invalid Receiver Type in Method Func

问题

我正在尝试创建一个简单的包来向服务器发送SSH命令。

我有以下代码:

type Connection *ssh.Client

func Connect(addr, user, password string) (conn Connection, err error) {
    sshConfig := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{
            ssh.Password(password),
        },
        HostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }),
    }

    conn, err = ssh.Dial("tcp", addr, sshConfig)
    return
}

func (conn Connection) SendCommand() ([]byte, error) {
    session, err := (*ssh.Client)(conn).NewSession()
    // ...
}

我的问题出现在这两行代码上:func (conn Connection) SendCommand() ([]byte, error)session, err := (*ssh.Client)(conn).NewSession()

我无法弄清楚如何在我的自定义Connection类型中使用*ssh.Client可用的方法。

我知道我需要进行一些转换,使用ssh.Client(*conn).NewSession()可以工作,但它会复制*ssh.Client的值,这似乎不是正确的方法。

在使用自定义的type Connection *ssh.Client类型时,我应该怎么做才能访问*ssh.Client可用的方法?

英文:

I'm trying to make a simple package to send SSH commands to a server.

I have the following code:

type Connection *ssh.Client

func Connect(addr, user, password string) (conn Connection, err error) {
	sshConfig := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
		HostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }),
	}

	conn, err = ssh.Dial("tcp", addr, sshConfig)
	return
}

func (conn Connection) SendCommand() ([]byte, error) {
	session, err := (*ssh.Client)(conn).NewSession()
	// ...
}

My problem is on the two lines func (conn Connection) SendCommand() ([]byte, error) and session, err := (*ssh.Client)(conn).NewSession().

I can't figure out how to use the methods available for *ssh.Client from my overlaying Connection type.

I understand that I need to do some conversion, and using ssh.Client(*conn).NewSession() would work, but it copies the values of the *ssh.Client which doesn't seem to be the right method.

What should do to access the methods available for a *ssh.Client when working with my custom type Connection *ssh.Client type?

答案1

得分: 43

你不能使用指针类型声明一个新的类型。此外,声明一个新类型的目的是为了移除整个方法集,所以你将无法使用*ssh.Client原有的任何方法。

你想要的是在你自己的结构体类型中嵌入*ssh.Client,以实现组合:

type Connection struct {
    *ssh.Client
}

func Connect(addr, user, password string) (*Connection, error) {
    sshConfig := &ssh.ClientConfig{
        User: user,
        Auth: []ssh.AuthMethod{
            ssh.Password(password),
        },
        HostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }),
    }

    conn, err := ssh.Dial("tcp", addr, sshConfig)
    if err != nil {
        return nil, err
    }

    return &Connection{conn}, nil
}

func (conn *Connection) SendCommand() ([]byte, error) {
    session, err := conn.NewSession()
    // ...
}
英文:

You can't declare a new type with a pointer TypeSpec. Also declaring a new type is used specifically to remove the entire method set, so you won't have any of the original methods from the *ssh.Client.

What you want is to use composition by embedding the *ssh.Client in your own struct type:

type Connection struct {
	*ssh.Client
}

func Connect(addr, user, password string) (*Connection, error) {
	sshConfig := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
		HostKeyCallback: ssh.HostKeyCallback(func(hostname string, remote net.Addr, key ssh.PublicKey) error { return nil }),
	}

	conn, err = ssh.Dial("tcp", addr, sshConfig)
	if err != nil {
		return nil, err
	}

	return &Connection{conn}, nil
}

func (conn *Connection) SendCommand() ([]byte, error) {
	session, err := conn.NewSession()
	// ...
}

答案2

得分: 4

这是我能想到的最好的解决方案:

type Connection ssh.Client

func (conn *Connection) SendCommand() ([]byte, error) {
    (*ssh.Client)(conn).NewSession()

请注意,我已经将类型更改为不是指针类型(但是我为SendCommand创建了一个指针接收器)。我不确定是否有任何方法可以创建一个具有指针类型作为接收器的函数。

英文:

This is the best I can come up with:

type Connection ssh.Client

func (conn *Connection) SendCommand() ([]byte, error) {
    (*ssh.Client)(conn).NewSession()

Note that I've changed the type to not be a pointer type (but then I've made a pointer receiver for SendCommand). I'm not sure there's any way to create a function with a pointer type as a receiver.

答案3

得分: 1

另一种选择是使用类型别名来实现所需的行为。我试图做一些“巧妙”的事情以提高可读性:

type foo struct {
	i int
}

type foo_ptr = *foo

type foo_ptr_slice = []foo_ptr
type foo_ptr_map = map[string]foo_ptr
type foo_ptr_slice_map = map[string]foo_ptr_slice

func (r foo_ptr) dump() {
	fmt.Printf("%d\n", r.i)
}

func main() {
	// 需要一个指针切片的映射
	var m foo_ptr_map

	m = make(foo_ptr_map, 0)

	m["test"] = &foo{i: 1}

	var m2 foo_ptr_slice_map

	m2 = make(foo_ptr_slice_map, 0)
	m2["test"] = make(foo_ptr_slice, 0, 10)

	m2["test"] = append(m2["test"], &foo{i: 2})

	fmt.Printf("%d\n", m["test"].i)
	fmt.Printf("%d\n", m2["test"][0].i)
	m["test"].dump()
}

我承认类型别名用于大规模重构,但这似乎是为了提高可读性而非常好的用法。

英文:

Another option is to use type aliasing to achieve the desired behavior. I was trying to do something "clever" for readability:

type foo struct {
	i int
}

type foo_ptr = *foo

type foo_ptr_slice = []foo_ptr
type foo_ptr_map = map[string]foo_ptr
type foo_ptr_slice_map = map[string]foo_ptr_slice

func (r foo_ptr) dump() {
	fmt.Printf("%d\n", r.i)
}

func main() {
	// need a map of slice of pointers
	var m foo_ptr_map

	m = make(foo_ptr_map, 0)

	m["test"] = &foo{i: 1}

	var m2 foo_ptr_slice_map

	m2 = make(foo_ptr_slice_map, 0)
	m2["test"] = make(foo_ptr_slice, 0, 10)

	m2["test"] = append(m2["test"], &foo{i: 2})

	fmt.Printf("%d\n", m["test"].i)
	fmt.Printf("%d\n", m2["test"][0].i)
	m["test"].dump()
}

I acknowledge that type aliasing is used for large-scale refactoring but this seems like a very good use for readability sake.

huangapple
  • 本文由 发表于 2017年6月7日 15:22:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/44406077.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定