Syntaxquestion (array of interface)

In a program I wrote I have to add a new row to a GTK Treeview, and it looks like this:

// Append video to list
iter := listStore.Append()
err := listStore.Set(iter, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
	[]interface{}{thumbnail,
		video.SubscriptionName,
		video.Added.Format(constDateLayout),
		video.Title,
		progress,
		backgroundColor,
		video.ID,
		duration,
		progressText,
		foregroundColor})

It works fine, but I am curios about why there is an extra set of curly braces in the last array ([]interface{}{…})? If you compare it with the second argument ([]int{…}) there is only one set of curly braces. I obviously just found some code that worked and just copied and pasted it. But now I am curious to why it works…

[Edit] Bold (using **) does not seem to work…?

[]int{0, 1, 2} says a slice ([]) of int and initialize it with {0, 1, 2}.

[]interface{}{0, 1, 2} says a slice of interface{} and initialize it with {0, 1, 2}.

Go can have anonymous types. You could also say something like:

stringers := []interface { String() string }{
    new(bytes.Buffer),
    big.NewFloat(123.456),
}

This part I get…

This I don’t fully understand, is the curly braces something that is only applicable to interfaces? I mean you could not write []int{}{0,1,2,3}, right?

I guess you can tell, that I am new to Go and I have not really studied interfaces yet…[Edit] and I probably should! :slight_smile:

Howabout an example that turned out to be a lot longer than I wanted it to be, but I think it’s hopefully still useful:

var i int

This declares a variable with the name i whose type is int. Declaring a string variable is similar:

var s string

The syntax is basically var name type. Let’s now define a type:

type MyInt int

This declares a new type with the name MyInt whose “underlying type” is a Go builtin int. “Underlying” here isn’t like a base class. It means that a MyInt is a new type, but it works the same as an int. You can do arithmetic, logical operations, bit shifts, etc., but the compiler now treats it as a different type from int.

Let’s look at a struct definition:

type MyStruct struct {
    S string
    I int
}

At first glance, this seems to work differently than MyInt above, but it actually doesn’t:

| `type` keyword | type name | underlying type            |
|----------------+-----------+----------------------------|
| type           | MyInt     | int                        |
| type           | MyStruct  | struct { S string; I int } |

It’s still just type name underlying_type like type MyInt int, but instead of the underlying type being a simple int, it’s now a struct { S string; I int }.

Just like you can do this with type definitions, you can do it with variables. Go has some neat features for handling constants that makes what I’m doing below unidiomatic, but if we get rid of the special constant handling, this is (for our purposes) more or less what the compiler sees:

var i int                    = int                    (5)
var s string                 = string                 ("Hello, world")
var x struct { X int; Y int} = struct { X int; Y int} {5, 6}

You can also declare and assign the variables with := like this (again pretending Go doesn’t have its special constant handling):

i := int(0)
s := string("Hello, World")
x := struct { X int; Y int} {5, 6}

Of course repeating the explicit struct type over and over would be horrible, which is why we usually define the struct into a type and use the type name like this:

x := MyStruct{5, 6}

The relationship between names and definitions of interface types works the same as struct types.

// MyStruct is a struct with a string field called S
// and then an int field called I.
type MyStruct struct {
    S string
    I int
}

// x is a variable with the same underlying type as MyStruct
// but it's not a MyStruct because we didn't say
//
//    var x MyStruct = ...
//
var x struct{ S string; I int} = struct{ S string; I int}{"Hi", 5}

// MyInterface is implemented by anything that has a String() function
// that returns a string.
type MyInterface interface {
    String() string
}

// y is a variable with the same underlying type as MyInterface.
// Interfaces are satisfied implicitly by using values that have the
// right method set that the interface needs, so this works:
var y interface{ String() string } = new(bytes.Buffer)

A lot of people feel like interface{} is a special type in Go, but really it’s the same thing as the y variable’s type definition above, just that interface{} doesn’t have any methods listed between its { and }, so it is unambiguously called the “empty interface.”

After all of that, let’s look back at (basically) your example:

var values []interface{} = []interface{}{
	thumbnail,
	video.SubscriptionName,
	video.Added.Format(constDateLayout),
	video.Title,
	progress,
	backgroundColor,
	video.ID,
	duration,
	progressText,
	foregroundColor,
}

This declares a variable with the name values whose type is a slice of interface{}s. It could also be a slice of interface{ String() string }s. It could also be a slice of ints.

It could also be – brace yourself! – a slice of struct { S string; I int }s:

var values []struct{ S string; I int} = []struct{ S string; I int}{
	{"A", 1},
	{"B", 2},
	{"C", 3},
}

There are only two situations where I use anonymous struct types:

  1. As the underlying type of a named struct type, i.e.:
    type MyStruct struct {
        A string
        B string
    }
    
  2. When the struct is empty:
    type singleton struct{}
    
    func (singleton) DoSomething() {
        panic("Using this singleton so it can implement an interface")
    }
    

It’s a lot of material, but I hope it makes when to use the braces clearer.

1 Like

Ah yes, empty interface, this I do remember reading about. So you can basically say that an interface{} can pretty much be anything, like an Object in C#, kind of? And of course, in my specific case, a GTK treeview, the developers of GTK does not want to limit the users of what they can put in the treeview, so they said, tell us how many columns you have ([]int), and then what you want each column to contain ([]interface{}). The last one being, anything, with any method signature…?

And since you can add images and texts and progress bars etc to the treeview, you can basically not say anything about what can be put in there, I guess.

Ps. Thanks for a long but very informative answer…this really was an “aha”-moment for me :slight_smile:

Right. My point is that the braces are there because []interface{} says “a slice of things that implement the empty interface.” Similarly []interface{ String() string } says "a slice of things that have a String() method that returns a string.

You can use type literals (like interface{}, interface{ String() string }, struct{ S string, I int}, etc.) or you can define your types so they have a name:

type Anything interface {
	// No methods needed to implement this interface.
	// It is the empty interface.
}

type Stringer interface {
	String() string
}

type MyStruct struct {
	S string
	I int
}

And then you can just use the names:

values := []Anything{
	thumbnail,
	video.SubscriptionName,
	video.Added.Format(constDateLayout),
	video.Title,
	progress,
	backgroundColor,
	video.ID,
	duration,
	progressText,
	foregroundColor,
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.