Reputation: 334
I'm trying to make a middle layer between two storages which fetches from storage A, converts it to a corresponding type of storage B and then stores it. As there are about 50-100 types I need to convert was I hoping to use a map[string]func
and based on the storageA.Type
determines which converting function I need to call.
Each of these converting functions will return different structs, all reflecting different types in storage B. Each of these storage B structs implements a common interface so they are possible to call functions with.
Boiled down my problem is that I can't cast func(StorageAType) StorageBType1
to func(StorageAType) StorageBType
even though StorageBtype1
implements the interface StorageBType
.
I've created this rather long playground as I realize describing the problem in words is tricky. Commenting out line 38-41 and 60-63 will make it run but it's those lines I want to use. Sorry for the size of it but I couldn't figure out a less verbose yet clear example.
Please note that I had to recreate my stackoverflow account so I don't think I have the rep to comment on answers.
*edit:
Very typical. Just after asking I realized how to solve it. By returning the interface type in the converter functions instead of the exact type made the change in this playground.
Upvotes: 4
Views: 120
Reputation: 417422
Function types with different result types are different types, it doesn't matter if one of the result type implements the other. Spec: Function types:
A function type denotes the set of all functions with the same parameter and result types.
StorageBtype1
and StorageBType
are different types, so the function types having them as their result types are also different, and a value of one of these function types cannot be used as the value for the other.
Simply change the result types of all converter functions to StorageBType
:
func TypeA3ToTypeB1(i StorageAType) StorageBType {
return StorageBType1{i.Type}
}
func TypeA5ToTypeB2(i StorageAType) StorageBType {
return StorageBType2{i.Type, i.Name}
}
Since all the return values implement StorageBType
, this is a valid change and requires no changes in the implementation of the converter functions.
Now of course calling these functions will return a value of type StorageBType
. If this is enough / sufficient for you, you have nothing further to do.
If by any chance you would need the return value as the concrete type that is stored in the interface value, you may use type assertion.
For example:
a := StorageAType{Type:3}
b := TypeA3ToTypeB1(a) // b is of type StorageBType
if b1, ok := b.(StorageBType1); ok {
// b1 is of type StorageBType1, you may use it like so:
fmt.Println("b1.Type:", b1.Type)
} else {
// b is not of type StorageBType1, or it is nil
}
Output:
b1.Type: 3
If you want to test for many concrete types, you may use a type switch:
switch i := b.(type) {
case nil:
fmt.Println("nil")
case StorageBType1:
// Here i is of type StorageBType1, you may refer to its fields:
fmt.Println("StorageBType1", i.Type)
case StorageBType2:
// Here i is of type StorageBType2, you may refer to its fields:
fmt.Println("StorageBType2", i.Type, i.Name)
default:
fmt.Println("Unhandled type!")
}
Upvotes: 2