I'm trying to write a simple server/client chat program for learning purposes but I'm stuck. I want to have the Leave
function remove the pointer it gets passed and update the slice in the struct so that the pointer is no longer there. But it's not working.
type Room struct {
Name string
Visitors []*net.Conn
}
func (r *Room) Leave(pc *net.Conn) {
for i, pv := range r.Visitors {
//found the connection we want to remove
if pc == pv {
fmt.Printf("Before %v
",r.Visitors)
r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...)
fmt.Printf("Before %v
",r.Visitors)
return
}
}
}
You are using a pointer to an interface (Visitors []*net.Conn
) in your Room
type. You should never need a pointer to an interface value. An interface is value is like a generic content holder and its in memory representation is different than a struct that implements that interface.
You should simply use declare the Visitors
type to be an interface:
type Room struct {
Name string
Visitors []net.Conn // should not be a pointer to interface value
}
func (r *Room) Leave(pc net.Conn) { // same thing as above
for i, pv := range r.Visitors {
// found the connection we want to remove
// in the comparison below actual concrete types are being compared.
// both these concrete types must be comparable (they can't be slices for example
if pc == pv {
r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...)
return
}
}
}
Note that the comparision above (pc == pv
) is not so trivial. Read about it here: https://golang.org/ref/spec#Comparison_operators
Also, refer to this question: Why can't I assign a *Struct to an *Interface?
The remove logic is correct (your output confirms this too). The problem is the lack of synchronization between multiple goroutines (your question says nothing about this).
You said (in your comment) that you wanted this to get working with 1 goroutine first, and deal with synchronization later. But your code already uses multiple goroutines, so you can't have that luxury:
//Let a goroutine Handle the connection
go handleConnection(conn)
Multiple goroutines are reading and writing the Room.Visitors
slice, so you have no choice but to synchronize access to it.
An example would be to use sync.RWLock
:
utils.go
:
type Room struct {
Name string
Visitors []net.Conn
mux sync.RWLock
}
func (r *Room) Leave(c net.Conn) {
r.mux.Lock()
defer r.mux.Unlock()
for i, v := range r.Visitors {
//found the connection we want to remove
if c == v {
fmt.Printf("Before remove %v
", r.Visitors)
r.Visitors = append(r.Visitors[:i], r.Visitors[i+1:]...)
fmt.Printf("After remove %v
", r.Visitors)
return
}
}
}
Also whenever any other code touches Room.Visitors
, also lock the code (you may use RWMutex.RLock()
if you're just reading it).
Similarly you need to synchronize access to all variables that are read/changed by multiple goroutines.
Also consider zeroing the element that is freed after the removal, else the underlying array will still hold that value, preventing the gargabe collector to properly free memory used by it. For more information about this, see Does go garbage collect parts of slices?