Type Assertion Panic on Int32 and Int64

655 views
Skip to first unread message

tim....@gmail.com

unread,
Jun 1, 2015, 10:50:51 AM6/1/15
to golan...@googlegroups.com

I am performing a type assertion (as my passed in type is map[string]interface{}. When I assign the value to a variable as such:

(*D).Memory = m["memory"].(int32)

I get the following panic:

panic: interface conversion: interface is int, not int32

goroutine 1 [running]:
runtime.panic(0x4a3c20, 0xc210047000)
/usr/lib/go/src/pkg/runtime/panic.c:266 +0xb6
test/device.(*Device).Init(0xc210046000, 0xc21001e390)
/home/user/Development/Go/src/test/device/device.go:112 +0x5fa
main.main()
/home/user/Development/Go/src/test/main.go:12 +0x201
exit status 2

If I change the value from int32 to int64 I get the same, but when I change the values to int along it all works fine. Given, I have to change the corresponding struct to int64's and int's as well, that is done.


I have been told this is not a bug, understanding why it isn't, would be nice.  Thanks

Paul Borman

unread,
Jun 1, 2015, 10:54:20 AM6/1/15
to tim....@gmail.com, golang-nuts
What is m?  I assume a map[string]interface{}?  In that case you can convert the interface into something other than its concrete type.  So:

t := m["memory].(int)
(*D).Memory = int32(t)

    -Paul

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tim Epkes

unread,
Jun 1, 2015, 11:17:41 AM6/1/15
to Paul Borman, golang-nuts
so m is what gets passed in and looks roughly like:

map[string]interface{}{"name":"rikers01","cpu":2,"memory":4096}

the workaround you have works, I am more trying to understand why a type assert to int32 or int64 is such a problem.  I am trying to keep my code compact and simple, so I want to reduce the clutter as it were.  Plus every conversion comes at a small cost and they pile up.  Thanks

Tim

Brad Fitzpatrick

unread,
Jun 1, 2015, 11:22:17 AM6/1/15
to Tim Epkes, Paul Borman, golang-nuts
You should really strive to avoid map[string]interface{} in the first place and use a proper type for your data.

Volker Dobler

unread,
Jun 1, 2015, 11:26:49 AM6/1/15
to golan...@googlegroups.com, tim....@gmail.com
Am Montag, 1. Juni 2015 16:50:51 UTC+2 schrieb tim....@gmail.com:

I am performing a type assertion (as my passed in type is map[string]interface{}. When I assign the value to a variable as such:

(*D).Memory = m["memory"].(int32)

I get the following panic:

panic: interface conversion: interface is int, not int32

If I change the value from int32 to int64 I get the same, but when I change the values to int along it all works fine. Given, I have to change the corresponding struct to int64's and int's as well, that is done.


I have been told this is not a bug, understanding why it isn't, would be nice.  Thanks


int, int32, int63 are all different types and have nothing in common but
the name (well, almost). You cannot type assert one into the other.

V. 

Tim Epkes

unread,
Jun 1, 2015, 11:27:21 AM6/1/15
to Brad Fitzpatrick, Paul Borman, golang-nuts
I am a python programmer traditionally and use defaults that have a dictionaries with different values in each field.  It is a very nice way to get defaults into a class.  I am trying to emulate that functionality in Go.  I find being constrained to a certain type constantly problematic.  The interface{} seemed like the right fit and from what I read is one of the things it does.  I also want to see if I get the flexibility of Python in Go.   So far it has been very accommodating, this particular aspect was a must have for me.  Are you saying it is not correct to use it?  

Thanks in advance

Tim Epkes

unread,
Jun 1, 2015, 11:32:21 AM6/1/15
to Volker Dobler, golang-nuts
I am actually not type asserting one for another.  My struct variable Memory is an int32 and I am trying to type assert into int32.  So int32 to int32.  But due to this issue, I have to either change my struct to int and then assert into int or I can keep my struct variable int32, type assert as int and then cast as int32.  

All I want to understand is why I can't type assert as int32 or int64?

Tim

Ian Lance Taylor

unread,
Jun 1, 2015, 11:42:44 AM6/1/15
to Tim Epkes, Volker Dobler, golang-nuts
On Mon, Jun 1, 2015 at 8:31 AM, Tim Epkes <tim....@gmail.com> wrote:
>
> I am actually not type asserting one for another. My struct variable Memory
> is an int32 and I am trying to type assert into int32. So int32 to int32.
> But due to this issue, I have to either change my struct to int and then
> assert into int or I can keep my struct variable int32, type assert as int
> and then cast as int32.
>
> All I want to understand is why I can't type assert as int32 or int64?

You can.

Show us a complete standalone example demonstrating the problem you
are encountering.

Ian

Andy Balholm

unread,
Jun 1, 2015, 12:13:13 PM6/1/15
to Tim Epkes, Volker Dobler, golang-nuts
There are two operations in Go that “change” a value from one type to another, and beginners often get them confused.

A type assertion—x.(type)—unwraps a value from an interface. The type specified must match the value’s type exactly, or it will fail. int32 and int are not exact matches, so your example fails. (Type assertions never change a value’s concrete type.)

A conversion—type(x)—actually converts a value from one type to another. It is normally used with concrete types, not interfaces.

The need to do both a type assertion and a conversion in your case is a result of Go’s policy of requiring explicit type conversions. C has a lot of implicit type conversion rules, and they are the source of confusing bugs.

Tim Epkes

unread,
Jun 1, 2015, 12:36:27 PM6/1/15
to Andy Balholm, Volker Dobler, golang-nuts
So basically the number I am passing 

map[string]interface{}{"name":"ultralight","model":"T40","cpu":2,"memory":4096}

in this case the memory, is equating to an int in the interfade? So when I try to assert a int32 it fails?  

is it that the number false in the range of an int on interface or is it that int is all interface supports?  Both would make sense.

Thanks in advance.

Tim

Paul Borman

unread,
Jun 1, 2015, 12:39:27 PM6/1/15
to Tim Epkes, Andy Balholm, Volker Dobler, golang-nuts
memory is an int, the map stores an interface wrapping the int (basically a type field and a pointer to the real int)

Ian Lance Taylor

unread,
Jun 1, 2015, 1:09:33 PM6/1/15
to Tim Epkes, Andy Balholm, Volker Dobler, golang-nuts
On Mon, Jun 1, 2015 at 9:35 AM, Tim Epkes <tim....@gmail.com> wrote:
>
> So basically the number I am passing
>
> map[string]interface{}{"name":"ultralight","model":"T40","cpu":2,"memory":4096}
>
> in this case the memory, is equating to an int in the interfade? So when I
> try to assert a int32 it fails?
>
> is it that the number false in the range of an int on interface or is it
> that int is all interface supports? Both would make sense.

You can store any type in an interface. But every value in an
interface has a specific type, and you must use that exact type when
writing a type assertion.

Constants in Go are untyped. You are storing an untyped 4096 into an
interface. In the absence of a type, the type selected for a integer
number is "int". See the paragraph on default type in
http://golang.org/ref/spec#Constants .

Ian

Tim Epkes

unread,
Jun 1, 2015, 1:22:02 PM6/1/15
to Ian Lance Taylor, Andy Balholm, Volker Dobler, golang-nuts
Thank you, that explains it.  Awesome, thank you all for the help in better understanding the implementation.  

Tim

Brendan Tracey

unread,
Jun 2, 2015, 2:29:31 AM6/2/15
to golan...@googlegroups.com, dr.volke...@gmail.com, tim....@gmail.com, andyb...@gmail.com, ia...@golang.org
Now that your question is answered, I'll address the other point.

While there exist exceptions, in general map[string]interface{} is not a good design decision. Usually, it would be better to do something like

type Config struct {
    Name string
    Model string
    CPU   int
    Memory int
}

If needed, you can then do

func DefaultConfig() *Config {
    return &Config {
        Name: "GopherPlushie",
        Model: "Pink",
        CPU: 8,
        Memory: 1024,
    }
}

And in the user code
config := pkg.DefaultConfig()
config.CPU = 16


Tim Epkes

unread,
Jun 2, 2015, 7:49:14 AM6/2/15
to Brendan Tracey, golang-nuts, Volker Dobler, Andy Balholm, ia...@golang.org
Brendan,

Thanks for the reply, definitely see that.  Let me put an example of what I am doing.  Sometimes I need a default based on model and the defaults are the pulled from that model.  Example code:

package main

import (
        "strconv"
        )

var defaults = map[string]map[string]string{
    "S93432": map[string]string{
        "vendor":"Sager",
        "memory":"16384",
        "cpu":"2"},
    "T40": map[string]string{
        "vendor":"Ibm",
        "memory":"2048",
        "cpu":"1"},
}

type Device struct{
    Name string
    Model string
    Vendor string
    Memory int32
    Cpu int
}

type Devices []Device

func keyinlist(s string, m map[string]interface{}) bool {
    for k := range m {
        if k == s {
            return true
        }
    }
    return false
}

func (D *Device) Init(m map[string]interface{}) {
        (*D).Name = m["name"].(string)
        (*D).Model = m["model"].(string)
        (*D).Vendor = defaults[m["model"].(string)]["vendor"]
        if keyinlist("memory",m) {
            (*D).Memory = m["memory"].(int32)
        } else {
            memory,_ := strconv.ParseInt(defaults[m["model"].(string)]["memory"],10,32)
            (*D).Memory = int32(memory)
        }
        if keyinlist("cpu",m) {
            (*D).Cpu = m["cpu"].(int)
        } else {
            cpu,_ := strconv.ParseInt(defaults[m["model"].(string)]["cpu"],10,32)
            (*D).Cpu = int(cpu)
        }
}

func main() {
    var d Devices
    mydev := new(Device)
    mydev.Init(map[string]interface{}{"name":"ultralight","model":"T40","cpu":2,"memory":4096})
    d = append(d, *mydev)
}


This makes it very easy to initialize a device based on its model type.  If a default configuration for a T40 laptop is 2G of mem and 2 CPU's, then it gets taken in at initialization.  Now this code is stripped down and I have checks to make sure you pass in a model and a name.  I can do this in Python very easily.  This code now works as well, but I certainly don't want to not use best practices, so how would this be done otherwise?

Thanks in advance

Tim

chris dollin

unread,
Jun 2, 2015, 8:38:23 AM6/2/15
to Tim Epkes, Brendan Tracey, golang-nuts, Volker Dobler, Andy Balholm, Ian Lance Taylor
On 2 June 2015 at 12:48, Tim Epkes <tim....@gmail.com> wrote:

(Long quote follows before comments)
Does that not strike you as being horribly complicated for the job it
seems to be doing? map[string]interface{} is very flexible but what you
have above is basically using it as a struct where every field access
needs a type assertion.

Here's my version of your code.

package main

import (
"fmt"
)

type Device struct{
Name string
Model string
Vendor string
Memory int32
Cpu int
}

// the defaults are just default Device values, so we may as well
// use the Device structs we already have.
var defaults = map[string]Device{
"S93432": Device{
Vendor: "Sager",
Memory: 16384,
Cpu: 2},
"T40": Device{
Vendor: "Ibm",
Memory: 2048,
Cpu: 1},
}

// Instead of a method may we well use a function that is given
// a possibly incomplete device and fills in the missing fields
// from the defaults.
func Init(d Device) Device {
// if there's no default for the model then we'll get the
// zero device value, which is OK. If it matters use the
// two-result version of [] and complain if there's no value
defaultDevice := defaults[d.Model]
// check each defaultable value and copy from default
if d.Cpu == 0 { d.Cpu = defaultDevice.Cpu }
if d.Memory == 0 { d.Memory = defaultDevice.Memory }
if d.Vendor == "" { d.Vendor = defaultDevice.Vendor }
return d
}

func main() {
// Make a (possibly partial) Device and fill it in from the defaults as per
// Init
d := []Device{Init(Device{Model: "T40", Name: "ultralight", Cpu:
2, Memory: 4096})}
fmt.Println(d)
}

No interface{}, no type assertions, no strconv, no keyinlist.

Chris

--
Chris "allusive" Dollin

Tim Epkes

unread,
Jun 2, 2015, 10:40:01 AM6/2/15
to chris dollin, Brendan Tracey, golang-nuts, Volker Dobler, Andy Balholm, Ian Lance Taylor
Thanks I'll give that a go.  It seem complicated in Go, but in Python it makes things extremely easy.  I see what you've done there, I'll try that and see if that works. That may be more efficient.  I guess the question then is, if interface{} is a bad practice/not recommended, why is it there?

Thanks in advance.

Tim

Brendan Tracey

unread,
Jun 2, 2015, 10:46:51 AM6/2/15
to Tim Epkes, chris dollin, golang-nuts, Volker Dobler, Andy Balholm, Ian Lance Taylor
There are some good uses of interface{}. A good example of it’s use is the fmt package. fmt can print arbitrary types, so interface{} is a good input parameter.

It’s map[string]interface{} that is usually a bad idea. The config struct approach will be faster, but that’s not the main benefit. The real power is that the config struct is compile-time safe, so you’ll never put the wrong type there, and the definition of the struct also provides documentation of the parameters. Any string can go into the map, so if a user wants to find what they can control, they have to trust that the doc comment is correct or they have to scour the code for uses of the map. In a config struct, all the available options (and what type the option is) can be seen right there in the definition of the struct.

chris dollin

unread,
Jun 2, 2015, 10:59:49 AM6/2/15
to Tim Epkes, Brendan Tracey, golang-nuts, Volker Dobler, Andy Balholm, Ian Lance Taylor
On 2 June 2015 at 15:39, Tim Epkes <tim....@gmail.com> wrote:
> Thanks I'll give that a go. It seem complicated in Go, but in Python it
> makes things extremely easy.

You pay a penalty for that apparent ease, both in run-time and
late error detection. Languages have their own strengths and
weaknesses; one has to play along them, not across them.

> I see what you've done there, I'll try that
> and see if that works. That may be more efficient. I guess the question
> then is, if interface{} is a bad practice/not recommended, why is it there?

It's there for the times when its a good thing, eg passing arbitrary
arguments to fmt.Println/Printf or a database etc.

Tim Epkes

unread,
Jun 2, 2015, 3:06:12 PM6/2/15
to chris dollin, Brendan Tracey, golang-nuts, Volker Dobler, Andy Balholm, Ian Lance Taylor
Thanks Chris and Brendan, that was very helpful.

Tim
Reply all
Reply to author
Forward
0 new messages