Golang在另一个结构体的方法中更改结构体的值

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

Golang changing values of a struct inside a method of another struct

问题

我有一个关于结构体的问题,也可能与指针有关,如果我猜得没错的话。

这个结构体有一些字段,还有一个保存切片的字段:

type Bot struct {
    // ...
    connlist []Connection
}

Connection 的定义如下:

type Connection struct {
    conn       net.Conn
    messages   int32
    channels   []string
    joins      int32
    connactive bool
}

我的问题是将 connactive 的值改为 true

Bot 有一个监听连接的方法:

func (bot *Bot) ListenToConnection(connection Connection) {
    reader := bufio.NewReader(connection.conn)
    tp := textproto.NewReader(reader)
    for {
        line, err := tp.ReadLine()
        if err != nil {
            log.Printf("Error reading from chat connection: %s", err)
            break // 在出现错误时跳出循环
        }
        if strings.Contains(line, "tmi.twitch.tv 001") {
            connection.activateConn()
        }
        if strings.Contains(line, "PING ") {
            fmt.Fprintf(connection.conn, "PONG tmi.twitch.tv\r\n")
        }
        fmt.Fprintf(bot.inconn, line+"\r\n")
    }
}

connection.activateConn() 是出现问题的部分,该方法的定义如下:

func (connection *Connection) activateConn() {
    connection.connactive = true
}

这部分实际上是被执行的,所以不是连接没有得到响应之类的问题。

但是,如果我稍后在 Bot 的一个方法中尝试循环遍历它,connactive 总是为 false(默认值)。

for i := 0; i < len(bot.connlist); i++ {
    log.Println(bot.connlist[i].connactive)
}

我认为我是在使用原始连接的副本,而不是已经更改为 connactive = true 的连接。

有什么想法吗?谢谢帮助。

英文:

I have an issue with structs and maybe an issue with pointers, if my guess is correct.

This struct has some fields and a field that holds a slice:

type Bot struct {
    // ...
    connlist []Connection
}

This Connection looks like this:

type Connection struct {
    conn       net.Conn
    messages   int32
    channels   []string
    joins      int32
    connactive bool
}

My problem is changing the value of connactive to true.

Bot has a method that listens to the connection:

func (bot *Bot) ListenToConnection(connection Connection) {
	reader := bufio.NewReader(connection.conn)
	tp := textproto.NewReader(reader)
	for {
		line, err := tp.ReadLine()
		if err != nil {
			log.Printf(&quot;Error reading from chat connection: %s&quot;, err)
			break // break loop on errors
		}
		if strings.Contains(line, &quot;tmi.twitch.tv 001&quot;) {
			connection.activateConn()
		}
		if strings.Contains(line, &quot;PING &quot;) {
			fmt.Fprintf(connection.conn, &quot;PONG tmi.twitch.tv\r\n&quot;)
		}
		fmt.Fprintf(bot.inconn, line+&quot;\r\n&quot;)
	}
}

And connection.activeConn() is the part that is not working correctly the method looks like this:

func (connection *Connection) activateConn() {
    connection.connactive = true
}

This actually gets executed so it's not an issue of the connection not getting a response or something.

But if I try to loop through it later in a method of Bot, connactive is always false for some reason (which is the default).

for i := 0; i &lt; len(bot.connlist); i++ {
		log.Println(bot.connlist[i].connactive)
}

I think I am working with a copy or so of the original connection and not the changed connection that has connactive = true.

Any ideas? Thanks for the help.

答案1

得分: 10

你的ListenToConnection()方法有一个参数:connection Connection

当你调用这个ListenToConnection()方法(你没有发布这段代码)时,你传递了一个Connection的值。在Go语言中,所有的东西都是按值传递的,所以会对传递的值进行复制。在ListenToConnection()内部,你操作的是这个副本。你调用了它的activateConn()方法,但是这个方法(它有一个指针接收器)将接收到这个副本的地址(一个局部变量)。

解决方法很简单,将ListenToConnection()的参数改为指针:

func (bot *Bot) ListenToConnection(connection *Connection) {
    // ...
}

使用Bot.connlist中的一个值调用它:

bot.ListenToConnection(&bot.connlist[0])

使用conlist中的每个元素进行调用的for循环:

for i := range bot.connlist {
    bot.ListenToConnection(&bot.conlist[i])
}

**注意!**我故意使用了一个只使用索引而不使用值的for ... range。如果使用带有索引和值的for ... range,或者只使用值,你会观察到相同的问题(connactive仍然为false):

for _, v := range bot.connlist {
    bot.ListenToConnection(&v) // 错误!v也是一个副本
}

因为v也只是一个副本,将它的地址传递给bot.ListenToConnection(),那只会指向副本,而不是connlist切片中的元素。

英文:

Your ListenToConnection() method has one parameter: connection Connection.

When you call this ListenToConnection() method (you didn't post this code), you pass a value of Connection. Everything in Go is passed by value, so a copy will be made of the passed value. Inside ListenToConnection() you operate with this copy. You call its activateConn() method, but that method (which has a pointer receiver) will receive the address of this copy (a local variable).

Solution is simple, change parameter of ListenToConnection() to be a pointer:

func (bot *Bot) ListenToConnection(connection *Connection) {
    // ...
}

Calling it with a value from Bot.connlist:

bot.ListenToConnection(&amp;bot.connlist[0])

A for loop calling it with every elements of conlist:

for i := range bot.connlist {
    bot.ListenToConnection(&amp;bot.conlist[i])
}

Attention! I intentionally used a for ... range which only uses the index and not the value. Using a for ... range with index and value, or just the value, you would observe the same issue (connactive would remain false):

for _, v := range bot.connlist {
    bot.ListenToConnection(&amp;v) // BAD! v is also a copy
}

Because v is also just a copy, passing its address to bot.ListenToConnection(), that would only point to the copy and not the element in the connlist slice.

答案2

得分: 1

需要将以下内容翻译为中文:

需要将连接的切片更改为指向连接的指针。
如果同时更改此属性,则需要使用信号量。

type Bot struct {
	// ...
	conns []*Connection
}

func (bot *Bot) ListenToConnection(c *Connection) {
   // 代码
}

type Connection struct {
	conn         net.Conn
	messages     int32
	channels     []string
	joins        int32
	isActive     bool
	isActiveLock sync.RWMutex
}

func (c *Connection) activateConn() {
	c.isActiveLock.Lock()
	defer c.isActiveLock.Unlock()
    
	c.isActive = true
}
英文:

It needs to be slice of pointers to connections.
And if this property will be changed concurrently, semaphore is necessary.

type Bot struct {
	// ...
	conns []*Connection
}

func (bot *Bot) ListenToConnection(c *Connection) {
   // code
}

type Connection struct {
	conn         net.Conn
	messages     int32
	channels     []string
	joins        int32
	isActive     bool
	isActiveLock sync.RWMutex
}

func (c *Connection) activateConn() {
	c.isActiveLock.Lock()
	defer c.isActiveLock.Unlock()
    
	c.isActive = true
}

huangapple
  • 本文由 发表于 2016年4月9日 21:25:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/36517373.html
匿名

发表评论

匿名网友

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

确定