In Go, an empty struct struct{}
is a struct with no fields that may appear to be of little use, but in reality, it can be useful in certain situations and become a simple and efficient solution in code.
As a semaphore or lock
Because the empty struct has no fields, it can be conveniently used to implement some concurrency control functions, such as mutex locks, read-write locks. We can use chan struct{}
to implement an unbuffered channel for controlling concurrent access.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
var mu sync.Mutex
c := make(chan struct{}, 1)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
c <- struct{}{} // acquire lock
defer func() {
<-c // release lock
wg.Done()
}()
mu.Lock()
defer mu.Unlock()
fmt.Printf("goroutine %d: hello\n", i)
}(i)
}
wg.Wait()
}
In the above code, we use an empty struct struct{}
to represent a semaphore. When a goroutine acquires the lock, it sends an empty struct to the channel to indicate that it has acquired the lock. After executing, it receives an empty struct from the channel to release the lock.
Placeholder
In some cases, we need to declare a variable or parameter but don't actually need to use it. In such cases, we can use an empty struct as a placeholder to avoid wasting memory. For example, we can use map[string]struct{} to represent a collection of key-value pairs, where an empty struct can be used to represent a case where we only need the key and not the value.
package main
import "fmt"
func main() {
m := map[string]struct{}{
"apple": {},
"banana": {},
"cherry": {},
"durian": {},
"elder": {},
"fig": {},
"grape": {},
"honeydew":{},
}
for k, _ := range m {
fmt.Println(k)
}
}
In the above code, we use an empty struct as the value of map[string]struct{} to indicate that we only need the key and not the value. This can avoid wasting memory and improve the readability of the code.
Interface Implementation
In Go, an interface is defined by implementing a set of methods. If a struct does not have any methods to implement but needs to implement an interface, an empty struct can be used as a placeholder to indicate that the struct implements the interface.
package main
import "fmt"
type myInterface interface{}
type myStruct struct{}
func (ms myStruct) String() string {
return "myStruct"
}
func main() {
var i myInterface = myStruct{}
fmt.Println(i)
}
In above code, we define an empty interface myInterface
, and a struct myStruct
with a String()
method that returns myStruct
. Then, we implement myInterface
by assigning myStruct{}
to the interface variable i
, making myStruct
a type that implements myInterface
.
In this example, since myInterface
is an empty interface with no methods, myStruct
as an implementation of myInterface does not need to implement any methods. However, since myStruct
implements the String()
method of the fmt.Stringer
interface, we can print the value of i using fmt.Println
and get the string representation of myStruct
.
In summary, the Go empty struct struct{}
is a simple and efficient solution that can be useful in certain situations. It can be used as a semaphore or lock, placeholder, and as a placeholder for implementing certain interfaces. Moreover, using an empty struct can avoid wasting memory and improve code readability.
The first code snippet is giving a deadlock error