Search code examples
gostructinterfacetype-assertion

Type Assertion using identical structs between packages


I am having a hard time understanding some type assertions in Go and why below code will not work and result in a panic.

panic: interface conversion: interface {} is []db.job, not []main.job

Main:

/*stackTypeAssert.go
> panic: interface conversion: interface {} is []db.job, not []main.job
*/

package main

import (
    "fmt"
    "stackTypeAssert/db"
)

type job struct {
    ID     int
    Status string
}

type jobs interface{}

func main() {
    jobTable := db.GetJobs()
    fmt.Println(jobTable) // This works: [{1 pending} {2 pending}]

    //Type Assertion
    var temp []job
    //panic: interface conversion: interface {} is []db.job, not []main.job
    temp = jobTable.([]job)
    fmt.Println(temp)
}

Package db:

/*Package db ...
panic: interface conversion: interface {} is []db.job, not []main.job
*/
package db

//GetJobs ...
func GetJobs() interface{} {
    //Job ...
    type job struct {
        ID     int
        Status string
    }
    task := &job{}
    var jobTable []job
    for i := 1; i < 3; i++ {
        *task = job{i, "pending"}
        jobTable = append(jobTable, *task)
    }
    return jobTable
}

Solution

  • In go lang spec for Import declarations it is described as:-

    The PackageName is used in qualified identifiers to access exported identifiers of the package within the importing source file. It is declared in the file block. If the PackageName is omitted, it defaults to the identifier specified in the package clause of the imported package. If an explicit period (.) appears instead of a name, all the package's exported identifiers declared in that package's package block will be declared in the importing source file's file block and must be accessed without a qualifier.

    As the error says:-

    panic: interface conversion: interface {} is []db.job, not []main.job

    You should use the struct of db package by importing it inside main to create temp variable, since the returned value is an interface wrapping the struct db.jobs not main.jobs

    package main
    
    import (
        "fmt"
        "stackTypeAssert/db"
    )
    
    type job struct {
        ID     int
        Status string
    }
    
    type jobs interface{}
    
    func main() {
        jobTable := db.GetJobs()
        fmt.Println(jobTable) // This works: [{1 pending} {2 pending}]
    
        // create a temp variable of []db.Job type
        var temp []db.Job
        // get the value of interface returned from `GetJobs` function in db package and then use type assertion to get the underlying slice of `db.Job` struct.
        temp = jobTable.(interface{}).([]db.Job)
        fmt.Println(temp)
    }
    

    In db package file define the struct outside GetJobs() function and make it exportable by converting the struct to uppercase.

    package db
    // make it exportable by converting the name of struct to uppercase
    type Job struct {
        ID     int
        Status string
    }
    
    //GetJobs ...
    func GetJobs() interface{} {
        task := &Job{}
        var jobTable []Job
        for i := 1; i < 3; i++ {
            *task = Job{i, "pending"}
            jobTable = append(jobTable, *task)
        }
        return jobTable
    }
    

    Output

    [{1 pending} {2 pending}]
    [{1 pending} {2 pending}]
    

    For more information on exported identifier, you can check this link Exported functions from another package