简体   繁体   中英

Getting pointer to struct in list in Go

I'm trying to make a function that will look through a list (type container/list) of elevator-structs and return a pointer to the elevator that has the correct ip-address (the program is meant to control elevators on multiple computers) or nil if there is no elevator with this address.

Here is the function code:

func (e *ElevatorList)IPIsInList(ip string) *elevator{
    for c := e.Elevators.Front(); c != nil; c = c.Next(){
        if(c.Value.(elevator).Ip == ip){
                return c.Value.(*elevator)
        }
    }
    return nil
}

I think the casting in the first return line looks kind of fishy but it's one of the few implementations that hasn't caused compiler errors. When I run the program I first get the output from other functions, and when the IPIsInList() function is called i get the following:

panic: interface conversion: interface is **main.elevator, not main.elevator

runtime.panic+0xac /home/fredrik/go/src/pkg/runtime/proc.c:1254
runtime.panic(0x4937a8, 0xf84002aaf0)
assertE2Tret+0x11d /home/fredrik/go/src/pkg/runtime/iface.c:312
assertE2Tret(0x4927f8, 0x45e6d8, 0xf8400004f0, 0x7fbae00c1ed8, 0x10, ...)
runtime.assertE2T+0x50 /home/fredrik/go/src/pkg/runtime/iface.c:292
runtime.assertE2T(0x4927f8, 0x45e6d8, 0xf8400004f0, 0x28)
main.(*ElevatorList).IPIsInList+0x5b /home/fredrik/Dropbox/Programmering/go/listtest/elevatorList.go:72
main.(*ElevatorList).IPIsInList(0xf8400001c8, 0x4a806c, 0x2e3332310000000f, 0x0, 0x0, ...)
main.main+0x1f3 /home/fredrik/Dropbox/Programmering/go/listtest/main.go:53
main.main()
runtime.mainstart+0xf /home/fredrik/go/src/pkg/runtime/amd64/asm.s:78
runtime.mainstart()
runtime.goexit /home/fredrik/go/src/pkg/runtime/proc.c:246
runtime.goexit()
----- goroutine created by -----
_rt0_amd64+0xc9 /home/fredrik/go/src/pkg/runtime/amd64/asm.s:65

How should this be done? I have rewritten the function many times and I think it's the use of c.Value.(elevator) and/or c.Value.(*elevator) that's causing the problem. Here are the structs for elevator and Elevator_list :

type elevator struct {
Ip string
OrderList [FLOORS][3]int32
Floor int32
Dir int
Ms_since_ping int32
}

type ElevatorList struct {

Elevators *list.List

}

The elevator s are added to the list with the function

func (e *ElevatorList) AddToList(newElevator *elevator){
e.Elevators.PushBack(&newElevator)

}

Obviously container/list works, but the answer to "How should this be done?" may be "don't use container/list, just use plain slices." Here's an alternative version:

package main

import "fmt"

const FLOORS = 6

type elevator struct {
    Ip            string
    OrderList     [FLOORS][3]int32
    Floor         int32
    Dir           int
    Ms_since_ping int32
}

type ElevatorList []*elevator

func (list ElevatorList) IPIsInList(ip string) *elevator {
    for _, e := range list {
        if e.Ip == ip {
            return e
        }
    }
    return nil
}

func (list ElevatorList) PrintAll() {
    fmt.Println(len(list), "items in list:")
    for _, e := range list {
        fmt.Println("  ", *e)
    }
}

func main() {
    var list ElevatorList
    list = append(list, &elevator{Ip: "1.1.1.1"})
    list = append(list, &elevator{Ip: "2.2.2.2"})
    list.PrintAll()
    list.PrintOne("1.1.1.1")
}

func (list ElevatorList) PrintOne(ip string) {
    if e := list.IPIsInList(ip); e == nil {
        fmt.Println(ip, "not found")
    } else {
        fmt.Println("found:", *e)
    }
}

Note that there was no need for ElevatorList to be a struct. I completely tossed the AddToList method. You might even toss IPIsInList. Without it the method PrintOne doesn't look any more complex.

func (list ElevatorList) PrintOne(ip string) {
    for _, e := range list {
        if e.Ip == ip {
            fmt.Println("found:", *e)
        }
    }
    fmt.Println(ip, "not found")
}

In your add function, you are taking a *elevator and taking the address of the pointer before putting it in your list. In your retrieve function you are asserting that the type is an elevator twice. Once in the if statement and once in the return. Both of them disagree with the true type ( *elevator). Your if statement is the first one so it panics explaining that the variable is an **elevator and not an elevator:

panic: interface conversion: interface is **main.elevator, not main.elevator

I would do two things. First, add *elevators to the list, not **elevators:

func (e *ElevatorList) AddToList(newElevator *elevator){
    e.Elevators.PushBack(newElevator)
}

Next, I would change the if statement so it does a type assertion to *elevator, not elevator:

if(*(c.Value.(*elevator)).Ip == ip){

For example,

package main

import (
    "container/list"
    "fmt"
)

type Elevator struct {
    Ip string
}

type Elevators struct {
    List *list.List
}

func (e *Elevators) AddToList(elevator *Elevator) {
    e.List.PushBack(elevator)
}

func (e *Elevators) IPIsInList(ip string) *Elevator {
    for c := e.List.Front(); c != nil; c = c.Next() {
        if c.Value.(*Elevator).Ip == ip {
            return c.Value.(*Elevator)
        }
    }
    return nil
}

func main() {
    elevators := Elevators{list.New()}
    ip := "192.168.0.10"
    elevator := Elevator{Ip: ip}
    elevators.AddToList(&elevator)
    eip := elevators.IPIsInList(ip)
    if eip != nil {
        fmt.Println(*eip)
    }
}

Output:

{192.168.0.10}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM