前往:如何从切片中删除元素并在内存中对其进行修改

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.

Example: Input,Output

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?