Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

anonymous struct and empty struct

People also ask

What is an empty struct?

An empty structIt occupies zero bytes of storage. var s struct{} fmt.Println(unsafe.Sizeof(s)) // prints 0. As the empty struct consumes zero bytes, it follows that it needs no padding. Thus a struct comprised of empty structs also consumes no storage.

What is an empty struct in C?

Empty struct in C is undefined behaviour (refer C17 spec, section 6.7. 2.1 ): If the struct-declaration-list does not contain any named members, either directly or via an anonymous structure or anonymous union, the behavior is undefined.

When should we use anonymous struct Golang?

When should I use an anonymous struct? I often use anonymous structs to marshal and unmarshal JSON data in HTTP handlers. If a struct is only meant to be used once, then it makes sense to declare it in such a way that developers down the road won't be tempted to accidentally use it again.

How do you declare an empty struct in Golang?

An empty struct is a struct type without fields struct{} . The cool thing about an empty structure is that it occupies zero bytes of storage. You can find an accurate description about the actual mechanism inside the golang compiler in this post by Dave Chaney.


Note that one interesting aspect of using struct{} for the type pushed to a channel (as opposed to int or bool), is that the size of an empty struct is... 0!

See the recent article "The empty struct" (March 2014) by Dave Cheney.

You can create as many struct{} as you want (struct{}{}) to push them to your channel: your memory won't be affected.
But you can use it for signaling between go routines, as illustrated in "Curious Channels".

finish := make(chan struct{})

As the behaviour of the close(finish) relies on signalling the close of the channel, not the value sent or received, declaring finish to be of type chan struct{} says that the channel contains no value; we’re only interested in its closed property.

And you retain all the other advantages linked to a struct:

  • you can define methods on it (that type can be a method receiver)
  • you can implement an interface (with said methods you just define on your empty struct)
  • as a singleton

in Go you can use an empty struct, and store all your data in global variables. There will only be one instance of the type, since all empty structs are interchangeable.

See for instance the global var errServerKeyExchange in the file where the empty struct rsaKeyAgreement is defined.


Composite literals

Composite literals construct values for structs, arrays, slices, and maps and create a new value each time they are evaluated. They consist of the type of the value followed by a brace-bound list of composite elements. An element may be a single expression or a key-value pair.

struct{}{} is a composite literal of type struct{}, the type of the value followed by a brace-bound list of composite elements.

for _ = range langs { <-done } is waiting until all the goroutines for all the langs have sent done messages.


  1. struct{} is a type (in particular, a structure with no members). If you have a type Foo, you can create a value of that type in an expression with Foo{field values, ...}. Putting this together, struct{}{} is a value of the type struct{}, which is what the channel expects.

  2. The main function spawns warrior goroutines, which will write to the done channel when they have finished. The last for block reads from this channel, ensuring that main won't return until all the goroutines have finished. This is important because the program will exit when main completes, irrespective of whether there are other goroutines running.


Good questions,

The whole point of the struct channel in this scenario is simply to signal the completion that something useful has happened. The channel type doesn't really matter, he could have used an int or a bool to accomplish the same effect. What's important is that his code is executing in a synchronized fashion where he's doing the necessary bookkeeping to signal and move on at key points.

I agree the syntax of struct{}{} looks odd at first because in this example he is declaring a struct and creating it in-line hence the second set of brackets.

If you had a pre-existing object like:

type Book struct{

}

You could create it like so: b := Book{}, you only need one set of brackets because the Book struct has already been declared.


done channel is used to receive notifications from warrior method that indicates the worker is done processing. So the channel can be anything, for example:

func warrior(name string, done chan bool) {
    select {
    case opponent := <-battle:
        fmt.Printf("%s beat %s\n", name, opponent)
    case battle <- name:
        // I lost :-(
    }
    done <- true
}

func main() {
    done := make(chan bool)
    langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"}
    for _, l := range langs { go warrior(l, done) }
    for _ = range langs { <-done }
}

We declare done := make(chan bool) as a channel that receives bool value, and send true at the end of warrior instead. This works! You can also define the done channel to any other type, it won't matter.

1. So what is with the weird done <- struct{}{}?

It is just another type that will be passed to channel. This is an empty struct, if you are familiar with the following:

type User struct {
    Name string
    Email string
}

struct{} makes no difference except it contains no fields, and struct{}{} is just an instance out of it. The best feature is it does not cost memory space!

2. for loop usage

We create 6 goroutines to run in the background with this line:

    for _, l := range langs { go warrior(l, done) }

We use the for _ = range langs { <-done }, because the main goroutine(where main function runs) does not wait for goroutins to finish.

If we does not include the last for line, chances are we see no outputs(because main goroutines quits before any child goroutines executes fmt.Printf code, and when main goroutine quits, all child goroutines will quit with it, and will not have any chance to run anyway).

So we wait for all goroutines to finish(it runs to the end, and send a message to the done channel), then exits. done channel here is a blocked channel, which means <-done will block here until a message is received from the channel.

We have 6 goroutines in the background, and use for loop, we wait until all goroutines send a message which means it finished running(because the done <-struct{}{} is at the the end of function).